22
33const bannedPackageName = "@effect-template/lib"
44
5+ /**
6+ * @typedef {{ readonly type: "Literal", readonly value: unknown } } LiteralSourceNode
7+ * @typedef {{ readonly value: { readonly cooked: string | null } } } TemplateQuasiNode
8+ * @typedef {{
9+ * readonly type: "TemplateLiteral",
10+ * readonly expressions: ReadonlyArray<unknown>,
11+ * readonly quasis: ReadonlyArray<TemplateQuasiNode>
12+ * }} TemplateLiteralSourceNode
13+ * @typedef {LiteralSourceNode | TemplateLiteralSourceNode } StaticSourceNode
14+ * @typedef {{ readonly type: "Identifier", readonly name: string } } IdentifierNode
15+ * @typedef {{ readonly type: "SpreadElement" } } SpreadElementNode
16+ */
17+
518/** @param {string } value */
619const isDirectLibImport = ( value ) =>
720 value === bannedPackageName || value . startsWith ( `${ bannedPackageName } /` )
821
9- /** @param {(import("eslint").JSSyntaxElement & { readonly value?: unknown }) | null | undefined } source */
10- const readSourceText = ( source ) => {
11- if ( source == null ) {
12- return null
13- }
22+ /**
23+ * @param {unknown } value
24+ * @returns {value is Record<string, unknown> }
25+ */
26+ const isRecord = ( value ) => typeof value === "object" && value !== null
27+
28+ /**
29+ * @param {unknown } value
30+ * @returns {value is LiteralSourceNode }
31+ */
32+ const isLiteralSourceNode = ( value ) =>
33+ isRecord ( value ) && value [ "type" ] === "Literal" && "value" in value
1434
15- if ( source . type === "Literal" && typeof source . value === "string" ) {
35+ /**
36+ * @param {unknown } value
37+ * @returns {value is TemplateLiteralSourceNode }
38+ */
39+ const isTemplateLiteralSourceNode = ( value ) =>
40+ isRecord ( value ) &&
41+ value [ "type" ] === "TemplateLiteral" &&
42+ Array . isArray ( value [ "expressions" ] ) &&
43+ Array . isArray ( value [ "quasis" ] )
44+
45+ /**
46+ * @param {unknown } value
47+ * @returns {value is IdentifierNode }
48+ */
49+ const isIdentifierNode = ( value ) =>
50+ isRecord ( value ) && value [ "type" ] === "Identifier" && typeof value [ "name" ] === "string"
51+
52+ /**
53+ * @param {unknown } value
54+ * @returns {value is SpreadElementNode }
55+ */
56+ const isSpreadElementNode = ( value ) =>
57+ isRecord ( value ) && value [ "type" ] === "SpreadElement"
58+
59+ /** @param {unknown } source */
60+ const readSourceText = ( source ) => {
61+ if ( isLiteralSourceNode ( source ) && typeof source . value === "string" ) {
1662 return source . value
1763 }
1864
1965 if (
20- source . type === "TemplateLiteral" &&
66+ isTemplateLiteralSourceNode ( source ) &&
2167 source . expressions . length === 0 &&
2268 source . quasis . length === 1
2369 ) {
@@ -33,7 +79,7 @@ const readSourceText = (source) => {
3379 * @returns {import("eslint").Rule.RuleListener }
3480 */
3581const createRuleListener = ( context ) => {
36- /** @param {(import("eslint").JSSyntaxElement & { readonly value?: unknown }) | null | undefined } source */
82+ /** @param {unknown } source */
3783 const checkSource = ( source ) => {
3884 if ( source == null ) {
3985 return
@@ -45,51 +91,51 @@ const createRuleListener = (context) => {
4591 }
4692
4793 context . report ( {
48- node : source ,
94+ node : /** @type { import("eslint").JSSyntaxElement } */ ( source ) ,
4995 messageId : "noLibImport" ,
5096 data : { source : sourceText }
5197 } )
5298 }
5399
54100 return {
55- /** @param {{ readonly callee?: import("eslint").JSSyntaxElement | null | undefined , readonly arguments?: ReadonlyArray<import("eslint").JSSyntaxElement | import("eslint").SpreadElement > | null | undefined } } node */
101+ /** @param {{ readonly callee?: unknown , readonly arguments?: ReadonlyArray<unknown > | null | undefined } } node */
56102 CallExpression ( node ) {
57103 if (
58- node . callee ?. type !== "Identifier" ||
104+ ! isIdentifierNode ( node . callee ) ||
59105 node . callee . name !== "require" ||
60106 ! Array . isArray ( node . arguments )
61107 ) {
62108 return
63109 }
64110
65111 const [ firstArgument ] = node . arguments
66- if ( firstArgument ?. type === "SpreadElement" ) {
112+ if ( isSpreadElementNode ( firstArgument ) ) {
67113 return
68114 }
69115
70116 checkSource ( firstArgument )
71117 } ,
72- /** @param {{ readonly source?: (import("eslint").JSSyntaxElement & { readonly value?: unknown }) | null | undefined } } node */
118+ /** @param {{ readonly source?: unknown } } node */
73119 ExportAllDeclaration ( node ) {
74120 checkSource ( node . source )
75121 } ,
76- /** @param {{ readonly source?: (import("eslint").JSSyntaxElement & { readonly value?: unknown }) | null | undefined } } node */
122+ /** @param {{ readonly source?: unknown } } node */
77123 ExportNamedDeclaration ( node ) {
78124 checkSource ( node . source )
79125 } ,
80- /** @param {{ readonly source?: (import("eslint").JSSyntaxElement & { readonly value?: unknown }) | null | undefined } } node */
126+ /** @param {{ readonly source?: unknown } } node */
81127 ImportDeclaration ( node ) {
82128 checkSource ( node . source )
83129 } ,
84- /** @param {{ readonly source?: (import("eslint").JSSyntaxElement & { readonly value?: unknown }) | null | undefined } } node */
130+ /** @param {{ readonly source?: unknown } } node */
85131 ImportExpression ( node ) {
86132 checkSource ( node . source )
87133 } ,
88- /** @param {{ readonly source?: (import("eslint").JSSyntaxElement & { readonly value?: unknown }) | null | undefined , readonly argument?: (import("eslint").JSSyntaxElement & { readonly value?: unknown }) | null | undefined } } node */
134+ /** @param {{ readonly source?: unknown, readonly argument?: unknown } } node */
89135 TSImportType ( node ) {
90136 checkSource ( "source" in node ? node . source : node . argument )
91137 } ,
92- /** @param {{ readonly expression?: (import("eslint").JSSyntaxElement & { readonly value?: unknown }) | null | undefined } } node */
138+ /** @param {{ readonly expression?: unknown } } node */
93139 TSExternalModuleReference ( node ) {
94140 checkSource ( node . expression )
95141 }
0 commit comments