diff --git a/.changeset/icy-turkeys-joke.md b/.changeset/icy-turkeys-joke.md deleted file mode 100644 index ba0d8f1f..00000000 --- a/.changeset/icy-turkeys-joke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@css-modules-kit/core': minor ---- - -feat!: remove `Cannot import module 'xxx'` error diff --git a/packages/codegen/src/project.test.ts b/packages/codegen/src/project.test.ts index efce1dee..5963b58d 100644 --- a/packages/codegen/src/project.test.ts +++ b/packages/codegen/src/project.test.ts @@ -91,10 +91,8 @@ describe('addFile', () => { // - The check stage cache for files that indirectly import the added file should also be invalidated. const iff = await createIFF({ 'tsconfig.json': '{ "cmkOptions": { "enabled": true } }', - 'src/a.module.css': '', - 'src/b.module.css': '@value a_1 from "./a.module.css";', // directly - 'src/re-export.module.css': '@import "./a.module.css";', - 'src/c.module.css': '@value a_1 from "./re-export.module.css";', // indirectly + 'src/b.module.css': '@import "./a.module.css";', // directly + 'src/c.module.css': '@value a_1 from "./b.module.css";', // indirectly }); const project = createProject({ project: iff.rootDir }); expect(formatDiagnostics(project.getDiagnostics(), iff.rootDir)).toMatchInlineSnapshot(` @@ -102,12 +100,12 @@ describe('addFile', () => { { "category": "error", "fileName": "/src/b.module.css", - "length": 3, + "length": 14, "start": { - "column": 8, + "column": 10, "line": 1, }, - "text": "Module './a.module.css' has no exported token 'a_1'.", + "text": "Cannot import module './a.module.css'", }, { "category": "error", @@ -117,12 +115,12 @@ describe('addFile', () => { "column": 8, "line": 1, }, - "text": "Module './re-export.module.css' has no exported token 'a_1'.", + "text": "Module './b.module.css' has no exported token 'a_1'.", }, ] `); await writeFile(iff.join('src/a.module.css'), '@value a_1: red;'); - project.updateFile(iff.join('src/a.module.css')); + project.addFile(iff.join('src/a.module.css')); expect(formatDiagnostics(project.getDiagnostics(), iff.rootDir)).toMatchInlineSnapshot(`[]`); }); test('changes the resolution results of import specifiers in other files', async () => { @@ -206,19 +204,19 @@ describe('updateFile', () => { `); // New semantic diagnostics are reported - await writeFile(iff.join('src/a.module.css'), `.a-1 {}`); + await writeFile(iff.join('src/a.module.css'), `@import './non-existent.module.css';`); project.updateFile(iff.join('src/a.module.css')); expect(formatDiagnostics(project.getDiagnostics(), iff.rootDir)).toMatchInlineSnapshot(` [ { "category": "error", "fileName": "/src/a.module.css", - "length": 3, + "length": 25, "start": { - "column": 2, + "column": 10, "line": 1, }, - "text": "css-modules-kit does not support invalid names as JavaScript identifiers.", + "text": "Cannot import module './non-existent.module.css'", }, ] `); @@ -313,9 +311,8 @@ describe('removeFile', () => { const iff = await createIFF({ 'tsconfig.json': '{ "cmkOptions": { "enabled": true } }', 'src/a.module.css': '@value a_1: red;', - 'src/b.module.css': '@value a_1 from "./a.module.css";', // directly - 'src/re-export.module.css': '@import "./a.module.css";', - 'src/c.module.css': '@value a_1 from "./re-export.module.css";', // indirectly + 'src/b.module.css': '@import "./a.module.css";', // directly + 'src/c.module.css': '@value a_1 from "./b.module.css";', // indirectly }); const project = createProject({ project: iff.rootDir }); expect(formatDiagnostics(project.getDiagnostics(), iff.rootDir)).toMatchInlineSnapshot(`[]`); @@ -323,6 +320,16 @@ describe('removeFile', () => { project.removeFile(iff.join('src/a.module.css')); expect(formatDiagnostics(project.getDiagnostics(), iff.rootDir)).toMatchInlineSnapshot(` [ + { + "category": "error", + "fileName": "/src/b.module.css", + "length": 14, + "start": { + "column": 10, + "line": 1, + }, + "text": "Cannot import module './a.module.css'", + }, { "category": "error", "fileName": "/src/c.module.css", @@ -331,7 +338,7 @@ describe('removeFile', () => { "column": 8, "line": 1, }, - "text": "Module './re-export.module.css' has no exported token 'a_1'.", + "text": "Module './b.module.css' has no exported token 'a_1'.", }, ] `); @@ -459,8 +466,8 @@ describe('getDiagnostics', () => { test('returns semantic diagnostics', async () => { const iff = await createIFF({ 'tsconfig.json': '{ "cmkOptions": { "enabled": true } }', - 'src/a.module.css': `.a-1 {}`, - 'src/b.module.css': `.b-1 {}`, + 'src/a.module.css': `@import './non-existent-1.module.css';`, + 'src/b.module.css': `@import './non-existent-2.module.css';`, }); const project = createProject({ project: iff.rootDir }); const diagnostics = project.getDiagnostics(); @@ -469,22 +476,22 @@ describe('getDiagnostics', () => { { "category": "error", "fileName": "/src/a.module.css", - "length": 3, + "length": 27, "start": { - "column": 2, + "column": 10, "line": 1, }, - "text": "css-modules-kit does not support invalid names as JavaScript identifiers.", + "text": "Cannot import module './non-existent-1.module.css'", }, { "category": "error", "fileName": "/src/b.module.css", - "length": 3, + "length": 27, "start": { - "column": 2, + "column": 10, "line": 1, }, - "text": "css-modules-kit does not support invalid names as JavaScript identifiers.", + "text": "Cannot import module './non-existent-2.module.css'", }, ] `); diff --git a/packages/core/src/checker.test.ts b/packages/core/src/checker.test.ts index 26749cac..b161624f 100644 --- a/packages/core/src/checker.test.ts +++ b/packages/core/src/checker.test.ts @@ -285,6 +285,59 @@ describe('checkCSSModule', () => { ] `); }); + test('report diagnostics for non-existing module', () => { + const args = prepareCheckerArgs([ + fakeCSSModule({ + fileName: '/a.module.css', + tokenImporters: [ + fakeAtImportTokenImporter({ from: './b.module.css', fromLoc: fakeLoc({ column: 1 }) }), + fakeAtValueTokenImporter({ + from: './c.module.css', + fromLoc: fakeLoc({ column: 2 }), + values: [fakeAtValueTokenImporterValue({ name: 'c_1', loc: fakeLoc({ column: 3 }) })], + }), + ], + }), + ]); + const diagnostics = checkCSSModule( + args.cssModules[0], + args.config, + args.exportBuilder, + args.matchesPattern, + args.resolver, + args.getCSSModule, + ); + expect(diagnostics).toMatchInlineSnapshot(` + [ + { + "category": "error", + "file": { + "fileName": "/a.module.css", + "text": "", + }, + "length": 0, + "start": { + "column": 1, + "line": 1, + }, + "text": "Cannot import module './b.module.css'", + }, + { + "category": "error", + "file": { + "fileName": "/a.module.css", + "text": "", + }, + "length": 0, + "start": { + "column": 2, + "line": 1, + }, + "text": "Cannot import module './c.module.css'", + }, + ] + `); + }); test('report diagnostics for non-exported token', () => { const args = prepareCheckerArgs([ fakeCSSModule({ diff --git a/packages/core/src/checker.ts b/packages/core/src/checker.ts index 647e1173..d23cd89e 100644 --- a/packages/core/src/checker.ts +++ b/packages/core/src/checker.ts @@ -8,6 +8,7 @@ import type { Location, MatchesPattern, Resolver, + TokenImporter, } from './type.js'; import { isValidAsJSIdentifier } from './util.js'; @@ -39,7 +40,10 @@ export function checkCSSModule( const from = resolver(tokenImporter.from, { request: cssModule.fileName }); if (!from || !matchesPattern(from)) continue; const imported = getCSSModule(from); - if (!imported) continue; + if (!imported) { + diagnostics.push(createCannotImportModuleDiagnostic(cssModule, tokenImporter)); + continue; + } if (tokenImporter.type === 'value') { const exportRecord = exportBuilder.build(imported); @@ -73,6 +77,16 @@ export function checkCSSModule( return diagnostics; } +function createCannotImportModuleDiagnostic(cssModule: CSSModule, tokenImporter: TokenImporter): Diagnostic { + return { + text: `Cannot import module '${tokenImporter.from}'`, + category: 'error', + file: { fileName: cssModule.fileName, text: cssModule.text }, + start: { line: tokenImporter.fromLoc.start.line, column: tokenImporter.fromLoc.start.column }, + length: tokenImporter.fromLoc.end.offset - tokenImporter.fromLoc.start.offset, + }; +} + function createModuleHasNoExportedTokenDiagnostic( cssModule: CSSModule, tokenImporter: AtValueTokenImporter, diff --git a/packages/ts-plugin/e2e-test/feature/semantic-diagnostics.test.ts b/packages/ts-plugin/e2e-test/feature/semantic-diagnostics.test.ts index ddd8cc12..7b8f7ae2 100644 --- a/packages/ts-plugin/e2e-test/feature/semantic-diagnostics.test.ts +++ b/packages/ts-plugin/e2e-test/feature/semantic-diagnostics.test.ts @@ -79,6 +79,20 @@ test('Semantic Diagnostics', async () => { }, "text": "Module './c.module.css' has no exported token 'c_3'.", }, + { + "category": "error", + "code": 0, + "end": { + "line": 5, + "offset": 24, + }, + "source": "css-modules-kit", + "start": { + "line": 5, + "offset": 10, + }, + "text": "Cannot import module './d.module.css'", + }, ] `); });