diff --git a/src/index.js b/src/index.js index 37d489c7..3aa747e2 100644 --- a/src/index.js +++ b/src/index.js @@ -26,7 +26,7 @@ import { } from './utils'; export default function loader(content, map, meta) { - const options = getOptions(this) || {}; + const options = getOptions(this); validateOptions(schema, options, { name: 'CSS Loader', diff --git a/src/plugins/postcss-import-parser.js b/src/plugins/postcss-import-parser.js index 94b1f10c..2f262f75 100644 --- a/src/plugins/postcss-import-parser.js +++ b/src/plugins/postcss-import-parser.js @@ -50,7 +50,7 @@ export default postcss.plugin(pluginName, (options) => (css, result) => { if (nodes[0].type === 'string') { isStringValue = true; url = nodes[0].value; - } else if (nodes[0].type === 'function') { + } else { // Invalid function - `@import nourl(test.css);` if (nodes[0].value.toLowerCase() !== 'url') { result.warn(`Unable to find uri in "${atRule.toString()}"`, { diff --git a/src/plugins/postcss-url-parser.js b/src/plugins/postcss-url-parser.js index 05732cae..ae8a7463 100644 --- a/src/plugins/postcss-url-parser.js +++ b/src/plugins/postcss-url-parser.js @@ -172,10 +172,9 @@ export default postcss.plugin(pluginName, (options) => (css, result) => { const { node, url, needQuotes, isStringValue } = rule; const splittedUrl = url.split(/(\?)?#/); const [urlWithoutHash, singleQuery, hashValue] = splittedUrl; - const hash = - singleQuery || hashValue - ? `${singleQuery ? '?' : ''}${hashValue ? `#${hashValue}` : ''}` - : ''; + + let hash = singleQuery ? '?' : ''; + hash += hashValue ? `#${hashValue}` : ''; let normalizedUrl = normalizeUrl( urlWithoutHash, diff --git a/test/__snapshots__/import-option.test.js.snap b/test/__snapshots__/import-option.test.js.snap index 8b33afde..213691d1 100644 --- a/test/__snapshots__/import-option.test.js.snap +++ b/test/__snapshots__/import-option.test.js.snap @@ -313,6 +313,68 @@ Array [ exports[`"import" option should respect style field in package.json: warnings 1`] = `Array []`; +exports[`"import" option should work resolve order: local -> node_modules -> alias: errors 1`] = `Array []`; + +exports[`"import" option should work resolve order: local -> node_modules -> alias: module 1`] = ` +"// Imports +import ___CSS_LOADER_API_IMPORT___ from \\"../../../src/runtime/api.js\\"; +import ___CSS_LOADER_AT_RULE_IMPORT_0___ from \\"-!../../../src/index.js??[ident]!./test.css\\"; +import ___CSS_LOADER_AT_RULE_IMPORT_1___ from \\"-!../../../src/index.js??[ident]!./issue-683.css\\"; +import ___CSS_LOADER_AT_RULE_IMPORT_2___ from \\"-!../../../src/index.js??[ident]!./node_modules/package/tilde.css\\"; +var ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(false); +___CSS_LOADER_EXPORT___.i(___CSS_LOADER_AT_RULE_IMPORT_0___); +___CSS_LOADER_EXPORT___.i(___CSS_LOADER_AT_RULE_IMPORT_1___); +___CSS_LOADER_EXPORT___.i(___CSS_LOADER_AT_RULE_IMPORT_2___); +// Module +___CSS_LOADER_EXPORT___.push([module.id, \\"\\\\n\\", \\"\\"]); +// Exports +export default ___CSS_LOADER_EXPORT___; +" +`; + +exports[`"import" option should work resolve order: local -> node_modules -> alias: result 1`] = ` +Array [ + Array [ + "../../src/index.js?[ident]!./import/test.css", + ".test { + a: a; +} +", + "", + ], + Array [ + "../../src/index.js?[ident]!./import/node_modules/issue-683/test.css", + ".test { + color: coral; +} +", + "", + ], + Array [ + "../../src/index.js?[ident]!./import/issue-683.css", + " +", + "", + ], + Array [ + "../../src/index.js?[ident]!./import/node_modules/package/tilde.css", + ".tilde { + color: yellow; +} +", + "", + ], + Array [ + "./import/import-order.css", + " +", + "", + ], +] +`; + +exports[`"import" option should work resolve order: local -> node_modules -> alias: warnings 1`] = `Array []`; + exports[`"import" option should work when "Function": errors 1`] = `Array []`; exports[`"import" option should work when "Function": module 1`] = ` diff --git a/test/__snapshots__/loader.test.js.snap b/test/__snapshots__/loader.test.js.snap index acc8a8fb..5a2ae929 100644 --- a/test/__snapshots__/loader.test.js.snap +++ b/test/__snapshots__/loader.test.js.snap @@ -579,6 +579,23 @@ a[href=\\"\\" i] { exports[`loader should work with empty options: warnings 1`] = `Array []`; +exports[`loader should work with none AST metadata: errors 1`] = `Array []`; + +exports[`loader should work with none AST metadata: result 1`] = ` +Array [ + Array [ + "./simple.css", + ".some-class { + color: red; +} +", + "", + ], +] +`; + +exports[`loader should work with none AST metadata: warnings 1`] = `Array []`; + exports[`loader should work with the "modules.auto" option and the "importLoaders" option: errors 1`] = `Array []`; exports[`loader should work with the "modules.auto" option and the "importLoaders" option: result 1`] = ` diff --git a/test/fixtures/import/import-order.css b/test/fixtures/import/import-order.css new file mode 100644 index 00000000..f758ecfa --- /dev/null +++ b/test/fixtures/import/import-order.css @@ -0,0 +1,3 @@ +@import "test"; +@import "issue-683"; +@import "aliasesPackage"; diff --git a/test/fixtures/import/import-order.js b/test/fixtures/import/import-order.js new file mode 100644 index 00000000..f596f02a --- /dev/null +++ b/test/fixtures/import/import-order.js @@ -0,0 +1,5 @@ +import css from './import-order.css'; + +__export__ = css; + +export default css; diff --git a/test/fixtures/import/node_modules/issue-683/package.json b/test/fixtures/import/node_modules/issue-683/package.json index 6b821392..2d5fb35c 100644 --- a/test/fixtures/import/node_modules/issue-683/package.json +++ b/test/fixtures/import/node_modules/issue-683/package.json @@ -3,7 +3,7 @@ "name": "test" }, "description": "test", - "main": "test.css", + "main": "test.js", "name": "test", "style": "test.css", "version": "1.0.0" diff --git a/test/fixtures/import/node_modules/test/package.json b/test/fixtures/import/node_modules/test/package.json new file mode 100644 index 00000000..8ebed9e8 --- /dev/null +++ b/test/fixtures/import/node_modules/test/package.json @@ -0,0 +1,9 @@ +{ + "author": { + "name": "test" + }, + "description": "test", + "main": "test.css", + "name": "test", + "version": "1.0.0" +} diff --git a/test/fixtures/import/node_modules/test/test.css b/test/fixtures/import/node_modules/test/test.css new file mode 100644 index 00000000..003440fd --- /dev/null +++ b/test/fixtures/import/node_modules/test/test.css @@ -0,0 +1,3 @@ +.test-package { + d: d +} diff --git a/test/helpers/getCompiler.js b/test/helpers/getCompiler.js index 5061f6de..7c20108a 100644 --- a/test/helpers/getCompiler.js +++ b/test/helpers/getCompiler.js @@ -35,6 +35,10 @@ export default (fixture, loaderOptions = {}, config = {}) => { }, resolve: { alias: { + aliasesPackage: path.resolve( + __dirname, + '../fixtures/import/node_modules/package/tilde.css' + ), aliasesImg: path.resolve(__dirname, '../fixtures/url'), aliasesImport: path.resolve(__dirname, '../fixtures/import'), aliasesComposes: path.resolve( diff --git a/test/helpers/preLoader.js b/test/helpers/preLoader.js new file mode 100644 index 00000000..8e624432 --- /dev/null +++ b/test/helpers/preLoader.js @@ -0,0 +1,5 @@ +export default function loader(content, map) { + const callback = this.async(); + + return callback(null, content, map, 'non-ast-meta'); +} diff --git a/test/import-option.test.js b/test/import-option.test.js index 7c172bfb..256446fd 100644 --- a/test/import-option.test.js +++ b/test/import-option.test.js @@ -153,6 +153,20 @@ describe('"import" option', () => { expect(getErrors(stats)).toMatchSnapshot('errors'); }); + it('should work resolve order: local -> node_modules -> alias', async () => { + const compiler = getCompiler('./import/import-order.js'); + const stats = await compile(compiler); + + expect(getModuleSource('./import/import-order.css', stats)).toMatchSnapshot( + 'module' + ); + expect(getExecutedCode('main.bundle.js', compiler, stats)).toMatchSnapshot( + 'result' + ); + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); + it('should emit warning when unresolved import', async () => { const compiler = getCompiler('./import/unresolved.js'); const stats = await compile(compiler); diff --git a/test/loader.test.js b/test/loader.test.js index b4d1a1df..115bcfd9 100644 --- a/test/loader.test.js +++ b/test/loader.test.js @@ -411,4 +411,35 @@ describe('loader', () => { expect(getWarnings(stats)).toMatchSnapshot('warnings'); expect(getErrors(stats)).toMatchSnapshot('errors'); }); + + it('should work with none AST metadata', async () => { + const compiler = getCompiler( + './simple.js', + {}, + { + module: { + rules: [ + { + test: /\.css$/i, + rules: [ + { + loader: path.resolve(__dirname, '../src'), + }, + { + loader: path.resolve(__dirname, '../test/helpers/preLoader'), + }, + ], + }, + ], + }, + } + ); + const stats = await compile(compiler); + + expect(getExecutedCode('main.bundle.js', compiler, stats)).toMatchSnapshot( + 'result' + ); + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + }); }); diff --git a/test/runtime/__snapshots__/api.test.js.snap b/test/runtime/__snapshots__/api.test.js.snap index 23ba2486..3304a234 100644 --- a/test/runtime/__snapshots__/api.test.js.snap +++ b/test/runtime/__snapshots__/api.test.js.snap @@ -2,6 +2,10 @@ exports[`api should import modules 1`] = `"body { b: 2; }body { c: 3; }body { b: 2; }@media print {body { b: 2; }}@media print {body { d: 4; }}@media screen and (orientation:landscape) {body { a: 1; }}@media (orientation:landscape) {body { a: 1; }}"`; +exports[`api should import modules when module string 1`] = `".button { b: 2; }"`; + +exports[`api should import modules with dedupe 1`] = `"body { b: 1; }body { b: 2; }.button { b: 3; }"`; + exports[`api should import named modules 1`] = `"body { b: 2; }body { c: 3; }body { b: 2; }@media print {body { b: 2; }}@media print {body { d: 4; }}@media screen {body { a: 1; }}"`; exports[`api should toString a single module 1`] = `"body { a: 1; }"`; diff --git a/test/runtime/api.test.js b/test/runtime/api.test.js index ced44a64..f945c4b2 100644 --- a/test/runtime/api.test.js +++ b/test/runtime/api.test.js @@ -137,4 +137,30 @@ describe('api', () => { expect(m.toString()).toMatchSnapshot(); }); + + it('should import modules with dedupe', () => { + const m = api(); + + const m1 = [null, 'body { b: 1; }', '']; + const m2 = ['./module2', 'body { b: 2; }', '']; + const m3 = ['./module3', '.button { b: 3; }', '']; + + m.i([m1], '', true); + m.i([m2], '', true); + m.i([m3], '', true); + m.i([m3], '', true); + m.i([m3], '', true); + + expect(m.toString()).toMatchSnapshot(); + expect(m.length).toBe(3); + }); + + it('should import modules when module string', () => { + const m = api(); + + m.i('.button { b: 2; }'); + m.i(''); + + expect(m.toString()).toMatchSnapshot(); + }); });