From 2adcca3d73135d4f6292c531f695603688da5a28 Mon Sep 17 00:00:00 2001 From: Evilebot Tnawi Date: Thu, 13 Dec 2018 20:58:41 +0300 Subject: [PATCH] style: use prettier (#644) --- .editorconfig | 18 +- .eslintignore | 2 +- .eslintrc.js | 17 +- .gitattributes | 3 + .gitignore | 27 +- .prettierrc | 5 + lib/formatSassError.js | 77 +-- lib/importsToResolve.js | 87 +-- lib/loader.js | 219 ++++--- lib/normalizeOptions.js | 110 ++-- lib/proxyCustomImporters.js | 25 +- lib/webpackImporter.js | 74 +-- package-lock.json | 6 + package.json | 1 + test/bootstrapSass/webpack.config.js | 69 +- test/extractText/webpack.config.js | 57 +- test/hmr/entry.js | 7 +- test/hmr/webpack.config.js | 46 +- test/index.test.js | 943 ++++++++++++++++----------- test/scss/another/module.js | 2 +- test/sourceMap/webpack.config.js | 56 +- test/spec.test.js | 63 +- test/tools/createSpec.js | 161 +++-- test/tools/customFunctions.js | 26 +- test/tools/customImporter.js | 14 +- test/tools/runCreateSpec.js | 8 +- test/tools/testLoader.js | 10 +- test/watch/entry.js | 4 +- test/watch/webpack.config.js | 60 +- 29 files changed, 1219 insertions(+), 978 deletions(-) create mode 100644 .gitattributes create mode 100644 .prettierrc diff --git a/.editorconfig b/.editorconfig index 5a6c4abe..9f89f364 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,15 +1,13 @@ -# This file is for unifying the coding style for different editors and IDEs. -# More information at http://EditorConfig.org - -# No .editorconfig files above the root directory -root = true +# editorconfig.org [*] -indent_style = space -indent_size = 4 charset = utf-8 -trim_trailing_whitespace = true +indent_style = space +indent_size = 2 +end_of_line = lf insert_final_newline = true +trim_trailing_whitespace = true -[package.json] -indent_size = 2 +[*.md] +insert_final_newline = true +trim_trailing_whitespace = false diff --git a/.eslintignore b/.eslintignore index f3ed9af4..561057a1 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,5 @@ +/node_modules # Compiled by webpack test/output - # Fake node_modules folder for tests test/node_modules diff --git a/.eslintrc.js b/.eslintrc.js index 786c97d3..07681eab 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,8 +1,13 @@ module.exports = { - root: true, - extends: ['@webpack-contrib/eslint-config-webpack'], - rules: { - // Remove strict rule in next major - "strict": "off", - }, + root: true, + plugins: ['prettier'], + extends: ['@webpack-contrib/eslint-config-webpack'], + rules: { + // Remove strict rule in next major + strict: 'off', + 'prettier/prettier': [ + 'error', + { singleQuote: true, trailingComma: 'es5', arrowParens: 'always' }, + ], + }, }; diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..70df4c97 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +package-lock.json -diff +* text=auto +bin/* eol=lf diff --git a/.gitignore b/.gitignore index a5f90616..c522c637 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,20 @@ -lib-cov -*.seed -*.log -*.csv -*.dat -*.out -*.pid -*.gz - -pids logs -results +*.log +npm-debug.log* +.eslintcache -npm-debug.log +/coverage +/dist +/local +/reports /node_modules -coverage + +.DS_Store +Thumbs.db .idea +*.iml +.vscode +*.sublime-project +*.sublime-workspace .nyc_output test/output diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..e537c8ad --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "singleQuote": true, + "trailingComma": "es5", + "arrowParens": "always" +} diff --git a/lib/formatSassError.js b/lib/formatSassError.js index 1e790502..1e992946 100644 --- a/lib/formatSassError.js +++ b/lib/formatSassError.js @@ -1,8 +1,8 @@ -"use strict"; +'use strict'; -const path = require("path"); -const os = require("os"); -const fs = require("fs"); +const path = require('path'); +const os = require('os'); +const fs = require('fs'); // A typical sass error looks like this // const SassError = { @@ -20,36 +20,37 @@ const fs = require("fs"); * @param {string} resourcePath */ function formatSassError(err, resourcePath) { - // Instruct webpack to hide the JS stack from the console - // Usually you're only interested in the SASS stack in this case. - // eslint-disable-next-line no-param-reassign - err.hideStack = true; - - // The file property is missing in rare cases. - // No improvement in the error is possible. - if (!err.file) { - return; - } + // Instruct webpack to hide the JS stack from the console + // Usually you're only interested in the SASS stack in this case. + // eslint-disable-next-line no-param-reassign + err.hideStack = true; - let msg = err.message; + // The file property is missing in rare cases. + // No improvement in the error is possible. + if (!err.file) { + return; + } - if (err.file === "stdin") { - // eslint-disable-next-line no-param-reassign - err.file = resourcePath; - } + let msg = err.message; - // node-sass returns UNIX-style paths + if (err.file === 'stdin') { // eslint-disable-next-line no-param-reassign - err.file = path.normalize(err.file); + err.file = resourcePath; + } - // The 'Current dir' hint of node-sass does not help us, we're providing - // additional information by reading the err.file property - msg = msg.replace(/\s*Current dir:\s*/, ""); + // node-sass returns UNIX-style paths + // eslint-disable-next-line no-param-reassign + err.file = path.normalize(err.file); - // eslint-disable-next-line no-param-reassign - err.message = `${getFileExcerptIfPossible(err) + - msg.charAt(0).toUpperCase() + msg.slice(1) + os.EOL - } in ${ err.file } (line ${ err.line }, column ${ err.column })`; + // The 'Current dir' hint of node-sass does not help us, we're providing + // additional information by reading the err.file property + msg = msg.replace(/\s*Current dir:\s*/, ''); + + // eslint-disable-next-line no-param-reassign + err.message = `${getFileExcerptIfPossible(err) + + msg.charAt(0).toUpperCase() + + msg.slice(1) + + os.EOL} in ${err.file} (line ${err.line}, column ${err.column})`; } /** @@ -62,17 +63,17 @@ function formatSassError(err, resourcePath) { * @returns {string} */ function getFileExcerptIfPossible(err) { - try { - const content = fs.readFileSync(err.file, "utf8"); + try { + const content = fs.readFileSync(err.file, 'utf8'); - return `${os.EOL + - content.split(os.EOL)[err.line - 1] + os.EOL + - new Array(err.column - 1).join(" ") }^${ os.EOL - } `; - } catch (ignoreErr) { - // If anything goes wrong here, we don't want any errors to be reported to the user - return ""; - } + return `${os.EOL + + content.split(os.EOL)[err.line - 1] + + os.EOL + + new Array(err.column - 1).join(' ')}^${os.EOL} `; + } catch (ignoreErr) { + // If anything goes wrong here, we don't want any errors to be reported to the user + return ''; + } } module.exports = formatSassError; diff --git a/lib/importsToResolve.js b/lib/importsToResolve.js index 2f53c370..a768d36a 100644 --- a/lib/importsToResolve.js +++ b/lib/importsToResolve.js @@ -1,8 +1,8 @@ -"use strict"; +'use strict'; -const path = require("path"); +const path = require('path'); -const utils = require("loader-utils"); +const utils = require('loader-utils'); const matchModuleImport = /^~([^/]+|@[^/]+[/][^/]+)$/; @@ -16,46 +16,47 @@ const matchModuleImport = /^~([^/]+|@[^/]+[/][^/]+)$/; * @returns {Array} */ function importsToResolve(url) { - const request = utils.urlToRequest(url); - // Keep in mind: ext can also be something like '.datepicker' when the true extension is omitted and the filename contains a dot. - // @see https://github.com/webpack-contrib/sass-loader/issues/167 - const ext = path.extname(request); - - if (matchModuleImport.test(url)) { - return [request, url]; - } - - // libsass' import algorithm works like this: - - // In case there is a file extension... - // - If the file is a CSS-file, do not include it all, but just link it via @import url(). - // - The exact file name must match (no auto-resolving of '_'-modules). - if (ext === ".css") { - return []; - } - if (ext === ".scss" || ext === ".sass") { - return [request, url]; - } - - // In case there is no file extension... - // - Prefer modules starting with '_'. - // - File extension precedence: .scss, .sass, .css. - const basename = path.basename(request); - - if (basename.charAt(0) === "_") { - return [ - `${ request }.scss`, `${ request }.sass`, `${ request }.css`, - url - ]; - } - - const dirname = path.dirname(request); - - return [ - `${ dirname }/_${ basename }.scss`, `${ dirname }/_${ basename }.sass`, `${ dirname }/_${ basename }.css`, - `${ request }.scss`, `${ request }.sass`, `${ request }.css`, - url - ]; + const request = utils.urlToRequest(url); + // Keep in mind: ext can also be something like '.datepicker' when the true extension is omitted and the filename contains a dot. + // @see https://github.com/webpack-contrib/sass-loader/issues/167 + const ext = path.extname(request); + + if (matchModuleImport.test(url)) { + return [request, url]; + } + + // libsass' import algorithm works like this: + + // In case there is a file extension... + // - If the file is a CSS-file, do not include it all, but just link it via @import url(). + // - The exact file name must match (no auto-resolving of '_'-modules). + if (ext === '.css') { + return []; + } + if (ext === '.scss' || ext === '.sass') { + return [request, url]; + } + + // In case there is no file extension... + // - Prefer modules starting with '_'. + // - File extension precedence: .scss, .sass, .css. + const basename = path.basename(request); + + if (basename.charAt(0) === '_') { + return [`${request}.scss`, `${request}.sass`, `${request}.css`, url]; + } + + const dirname = path.dirname(request); + + return [ + `${dirname}/_${basename}.scss`, + `${dirname}/_${basename}.sass`, + `${dirname}/_${basename}.css`, + `${request}.scss`, + `${request}.sass`, + `${request}.css`, + url, + ]; } module.exports = importsToResolve; diff --git a/lib/loader.js b/lib/loader.js index 715acf59..082a2e63 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -1,16 +1,14 @@ -"use strict"; +'use strict'; -const path = require("path"); +const path = require('path'); -const async = require("neo-async"); +const async = require('neo-async'); +const pify = require('pify'); +const semver = require('semver'); -const pify = require("pify"); - -const semver = require("semver"); - -const formatSassError = require("./formatSassError"); -const webpackImporter = require("./webpackImporter"); -const normalizeOptions = require("./normalizeOptions"); +const formatSassError = require('./formatSassError'); +const webpackImporter = require('./webpackImporter'); +const normalizeOptions = require('./normalizeOptions'); let nodeSassJobQueue = null; @@ -21,76 +19,84 @@ let nodeSassJobQueue = null; * @param {string} content */ function sassLoader(content) { - const callback = this.async(); - const isSync = typeof callback !== "function"; - const self = this; - const { resourcePath } = this; - - function addNormalizedDependency(file) { - // node-sass returns POSIX paths - self.dependency(path.normalize(file)); - } + const callback = this.async(); + const isSync = typeof callback !== 'function'; + const self = this; + const { resourcePath } = this; + + function addNormalizedDependency(file) { + // node-sass returns POSIX paths + self.dependency(path.normalize(file)); + } + + if (isSync) { + throw new Error( + 'Synchronous compilation is not supported anymore. See https://github.com/webpack-contrib/sass-loader/issues/333' + ); + } + + const options = normalizeOptions( + this, + content, + webpackImporter( + resourcePath, + pify(this.resolve.bind(this)), + addNormalizedDependency + ) + ); + + // Skip empty files, otherwise it will stop webpack, see issue #21 + if (options.data.trim() === '') { + callback(null, ''); + return; + } + + const render = getRenderFuncFromSassImpl( + // eslint-disable-next-line import/no-extraneous-dependencies, global-require + options.implementation || require('node-sass') + ); - if (isSync) { - throw new Error("Synchronous compilation is not supported anymore. See https://github.com/webpack-contrib/sass-loader/issues/333"); - } + render(options, (err, result) => { + if (err) { + formatSassError(err, this.resourcePath); - const options = normalizeOptions(this, content, webpackImporter( - resourcePath, - pify(this.resolve.bind(this)), - addNormalizedDependency - )); + if (err.file) { + this.dependency(err.file); + } - // Skip empty files, otherwise it will stop webpack, see issue #21 - if (options.data.trim() === "") { - callback(null, ""); - return; + callback(err); + return; } - // eslint-disable-next-line import/no-extraneous-dependencies, global-require - const render = getRenderFuncFromSassImpl(options.implementation || require("node-sass")); - - render(options, (err, result) => { - if (err) { - formatSassError(err, this.resourcePath); - - if (err.file) { - this.dependency(err.file); - } - - callback(err); - return; - } - - if (result.map && result.map !== "{}") { - // eslint-disable-next-line no-param-reassign - result.map = JSON.parse(result.map); - // result.map.file is an optional property that provides the output filename. - // Since we don't know the final filename in the webpack build chain yet, it makes no sense to have it. - // eslint-disable-next-line no-param-reassign - delete result.map.file; - // The first source is 'stdin' according to node-sass because we've used the data input. - // Now let's override that value with the correct relative path. - // Since we specified options.sourceMap = path.join(process.cwd(), "/sass.map"); in normalizeOptions, - // we know that this path is relative to process.cwd(). This is how node-sass works. - // eslint-disable-next-line no-param-reassign - result.map.sources[0] = path.relative(process.cwd(), resourcePath); - // node-sass returns POSIX paths, that's why we need to transform them back to native paths. - // This fixes an error on windows where the source-map module cannot resolve the source maps. - // @see https://github.com/webpack-contrib/sass-loader/issues/366#issuecomment-279460722 - // eslint-disable-next-line no-param-reassign - result.map.sourceRoot = path.normalize(result.map.sourceRoot); - // eslint-disable-next-line no-param-reassign - result.map.sources = result.map.sources.map(path.normalize); - } else { - // eslint-disable-next-line no-param-reassign - result.map = null; - } - - result.stats.includedFiles.forEach(addNormalizedDependency); - - callback(null, result.css.toString(), result.map); - }); + if (result.map && result.map !== '{}') { + // eslint-disable-next-line no-param-reassign + result.map = JSON.parse(result.map); + // result.map.file is an optional property that provides the output filename. + // Since we don't know the final filename in the webpack build chain yet, it makes no sense to have it. + // eslint-disable-next-line no-param-reassign + delete result.map.file; + // The first source is 'stdin' according to node-sass because we've used the data input. + // Now let's override that value with the correct relative path. + // Since we specified options.sourceMap = path.join(process.cwd(), "/sass.map"); in normalizeOptions, + // we know that this path is relative to process.cwd(). This is how node-sass works. + // eslint-disable-next-line no-param-reassign + result.map.sources[0] = path.relative(process.cwd(), resourcePath); + // node-sass returns POSIX paths, that's why we need to transform them back to native paths. + // This fixes an error on windows where the source-map module cannot resolve the source maps. + // @see https://github.com/webpack-contrib/sass-loader/issues/366#issuecomment-279460722 + // eslint-disable-next-line no-param-reassign + result.map.sourceRoot = path.normalize(result.map.sourceRoot); + // eslint-disable-next-line no-param-reassign + result.map.sources = result.map.sources.map(path.normalize); + } else { + // eslint-disable-next-line no-param-reassign + result.map = null; + } + + result.stats.includedFiles.forEach(addNormalizedDependency); + + callback(null, result.css.toString(), result.map); + }); } /** @@ -100,43 +106,50 @@ function sassLoader(content) { * @returns {Function} */ function getRenderFuncFromSassImpl(module) { - const { info } = module; - const components = info.split("\t"); - - if (components.length < 2) { - throw new Error(`Unknown Sass implementation "${ info }".`); - } + const { info } = module; + const components = info.split('\t'); - const [implementation, version] = components; + if (components.length < 2) { + throw new Error(`Unknown Sass implementation "${info}".`); + } - if (!semver.valid(version)) { - throw new Error(`Invalid Sass version "${ version }".`); - } + const [implementation, version] = components; - if (implementation === "dart-sass") { - if (!semver.satisfies(version, "^1.3.0")) { - throw new Error(`Dart Sass version ${ version } is incompatible with ^1.3.0.`); - } + if (!semver.valid(version)) { + throw new Error(`Invalid Sass version "${version}".`); + } - return module.render.bind(module); - } else if (implementation === "node-sass") { - if (!semver.satisfies(version, "^4.0.0")) { - throw new Error(`Node Sass version ${ version } is incompatible with ^4.0.0.`); - } + if (implementation === 'dart-sass') { + if (!semver.satisfies(version, '^1.3.0')) { + throw new Error( + `Dart Sass version ${version} is incompatible with ^1.3.0.` + ); + } - // There is an issue with node-sass when async custom importers are used - // See https://github.com/sass/node-sass/issues/857#issuecomment-93594360 - // We need to use a job queue to make sure that one thread is always available to the UV lib - if (nodeSassJobQueue === null) { - const threadPoolSize = Number(process.env.UV_THREADPOOL_SIZE || 4); + return module.render.bind(module); + } else if (implementation === 'node-sass') { + if (!semver.satisfies(version, '^4.0.0')) { + throw new Error( + `Node Sass version ${version} is incompatible with ^4.0.0.` + ); + } - nodeSassJobQueue = async.queue(module.render.bind(module), threadPoolSize - 1); - } + // There is an issue with node-sass when async custom importers are used + // See https://github.com/sass/node-sass/issues/857#issuecomment-93594360 + // We need to use a job queue to make sure that one thread is always available to the UV lib + if (nodeSassJobQueue === null) { + const threadPoolSize = Number(process.env.UV_THREADPOOL_SIZE || 4); - return nodeSassJobQueue.push.bind(nodeSassJobQueue); + nodeSassJobQueue = async.queue( + module.render.bind(module), + threadPoolSize - 1 + ); } - throw new Error(`Unknown Sass implementation "${ implementation }".`); + return nodeSassJobQueue.push.bind(nodeSassJobQueue); + } + + throw new Error(`Unknown Sass implementation "${implementation}".`); } module.exports = sassLoader; diff --git a/lib/normalizeOptions.js b/lib/normalizeOptions.js index 363cf662..c7ef5c17 100644 --- a/lib/normalizeOptions.js +++ b/lib/normalizeOptions.js @@ -1,12 +1,12 @@ -"use strict"; +'use strict'; -const os = require("os"); -const path = require("path"); +const os = require('os'); +const path = require('path'); -const utils = require("loader-utils"); -const cloneDeep = require("clone-deep"); +const utils = require('loader-utils'); +const cloneDeep = require('clone-deep'); -const proxyCustomImporters = require("./proxyCustomImporters"); +const proxyCustomImporters = require('./proxyCustomImporters'); /** * Derives the sass options from the loader context and normalizes its values with sane defaults. @@ -20,61 +20,67 @@ const proxyCustomImporters = require("./proxyCustomImporters"); * @returns {Object} */ function normalizeOptions(loaderContext, content, webpackImporter) { - const options = cloneDeep(utils.getOptions(loaderContext)) || {}; - const { resourcePath } = loaderContext; + const options = cloneDeep(utils.getOptions(loaderContext)) || {}; + const { resourcePath } = loaderContext; - options.data = options.data ? (options.data + os.EOL + content) : content; + options.data = options.data ? options.data + os.EOL + content : content; - // opt.outputStyle - if (!options.outputStyle && loaderContext.minimize) { - options.outputStyle = "compressed"; - } + // opt.outputStyle + if (!options.outputStyle && loaderContext.minimize) { + options.outputStyle = 'compressed'; + } - // opt.sourceMap - // Not using the `this.sourceMap` flag because css source maps are different - // @see https://github.com/webpack/css-loader/pull/40 - if (options.sourceMap) { - // Deliberately overriding the sourceMap option here. - // node-sass won't produce source maps if the data option is used and options.sourceMap is not a string. - // In case it is a string, options.sourceMap should be a path where the source map is written. - // But since we're using the data option, the source map will not actually be written, but - // all paths in sourceMap.sources will be relative to that path. - // Pretty complicated... :( - options.sourceMap = path.join(process.cwd(), "/sass.map"); - if ("sourceMapRoot" in options === false) { - options.sourceMapRoot = process.cwd(); - } - if ("omitSourceMapUrl" in options === false) { - // The source map url doesn't make sense because we don't know the output path - // The css-loader will handle that for us - options.omitSourceMapUrl = true; - } - if ("sourceMapContents" in options === false) { - // If sourceMapContents option is not set, set it to true otherwise maps will be empty/null - // when exported by webpack-extract-text-plugin. - options.sourceMapContents = true; - } + // opt.sourceMap + // Not using the `this.sourceMap` flag because css source maps are different + // @see https://github.com/webpack/css-loader/pull/40 + if (options.sourceMap) { + // Deliberately overriding the sourceMap option here. + // node-sass won't produce source maps if the data option is used and options.sourceMap is not a string. + // In case it is a string, options.sourceMap should be a path where the source map is written. + // But since we're using the data option, the source map will not actually be written, but + // all paths in sourceMap.sources will be relative to that path. + // Pretty complicated... :( + options.sourceMap = path.join(process.cwd(), '/sass.map'); + if ('sourceMapRoot' in options === false) { + options.sourceMapRoot = process.cwd(); + } + if ('omitSourceMapUrl' in options === false) { + // The source map url doesn't make sense because we don't know the output path + // The css-loader will handle that for us + options.omitSourceMapUrl = true; } + if ('sourceMapContents' in options === false) { + // If sourceMapContents option is not set, set it to true otherwise maps will be empty/null + // when exported by webpack-extract-text-plugin. + options.sourceMapContents = true; + } + } - // indentedSyntax is a boolean flag. - const ext = path.extname(resourcePath); + // indentedSyntax is a boolean flag. + const ext = path.extname(resourcePath); - // If we are compiling sass and indentedSyntax isn't set, automatically set it. - if (ext && ext.toLowerCase() === ".sass" && "indentedSyntax" in options === false) { - options.indentedSyntax = true; - } else { - options.indentedSyntax = Boolean(options.indentedSyntax); - } + // If we are compiling sass and indentedSyntax isn't set, automatically set it. + if ( + ext && + ext.toLowerCase() === '.sass' && + 'indentedSyntax' in options === false + ) { + options.indentedSyntax = true; + } else { + options.indentedSyntax = Boolean(options.indentedSyntax); + } - // Allow passing custom importers to `node-sass`. Accepts `Function` or an array of `Function`s. - options.importer = options.importer ? proxyCustomImporters(options.importer, resourcePath) : []; - options.importer.push(webpackImporter); + // Allow passing custom importers to `node-sass`. Accepts `Function` or an array of `Function`s. + options.importer = options.importer + ? proxyCustomImporters(options.importer, resourcePath) + : []; + options.importer.push(webpackImporter); - // `node-sass` uses `includePaths` to resolve `@import` paths. Append the currently processed file. - options.includePaths = options.includePaths || []; - options.includePaths.push(path.dirname(resourcePath)); + // `node-sass` uses `includePaths` to resolve `@import` paths. Append the currently processed file. + options.includePaths = options.includePaths || []; + options.includePaths.push(path.dirname(resourcePath)); - return options; + return options; } module.exports = normalizeOptions; diff --git a/lib/proxyCustomImporters.js b/lib/proxyCustomImporters.js index e107ea5b..43bbb2c7 100644 --- a/lib/proxyCustomImporters.js +++ b/lib/proxyCustomImporters.js @@ -1,4 +1,4 @@ -"use strict"; +'use strict'; /** * Creates new custom importers that use the given `resourcePath` if libsass calls the custom importer with `prev` @@ -15,16 +15,19 @@ * @returns {Array} */ function proxyCustomImporters(importer, resourcePath) { - return [].concat(importer).map( - // eslint-disable-next-line no-shadow - (importer) => function customImporter() { - return importer.apply( - this, - // eslint-disable-next-line prefer-rest-params - Array.from(arguments) - .map((arg, i) => i === 1 && arg === "stdin" ? resourcePath : arg) - ); - }); + return [].concat(importer).map( + // eslint-disable-next-line no-shadow + (importer) => + function customImporter() { + return importer.apply( + this, + // eslint-disable-next-line prefer-rest-params + Array.from(arguments).map((arg, i) => + i === 1 && arg === 'stdin' ? resourcePath : arg + ) + ); + } + ); } module.exports = proxyCustomImporters; diff --git a/lib/webpackImporter.js b/lib/webpackImporter.js index 5fc2e7c0..afa27113 100644 --- a/lib/webpackImporter.js +++ b/lib/webpackImporter.js @@ -1,4 +1,4 @@ -"use strict"; +'use strict'; /** * @name PromisedResolve @@ -16,11 +16,11 @@ * @param {Function} done */ -const path = require("path"); +const path = require('path'); -const tail = require("lodash.tail"); +const tail = require('lodash.tail'); -const importsToResolve = require("./importsToResolve"); +const importsToResolve = require('./importsToResolve'); const matchCss = /\.css$/; @@ -36,41 +36,41 @@ const matchCss = /\.css$/; * @returns {Importer} */ function webpackImporter(resourcePath, resolve, addNormalizedDependency) { - function dirContextFrom(fileContext) { - return path.dirname( - // The first file is 'stdin' when we're using the data option - fileContext === "stdin" ? resourcePath : fileContext - ); - } + function dirContextFrom(fileContext) { + return path.dirname( + // The first file is 'stdin' when we're using the data option + fileContext === 'stdin' ? resourcePath : fileContext + ); + } - // eslint-disable-next-line no-shadow - function startResolving(dir, importsToResolve) { - return importsToResolve.length === 0 ? - Promise.reject() : - resolve(dir, importsToResolve[0]) - .then(resolvedFile => { - // Add the resolvedFilename as dependency. Although we're also using stats.includedFiles, this might come - // in handy when an error occurs. In this case, we don't get stats.includedFiles from node-sass. - addNormalizedDependency(resolvedFile); - return { - // By removing the CSS file extension, we trigger node-sass to include the CSS file instead of just linking it. - file: resolvedFile.replace(matchCss, "") - }; - }, () => startResolving( - dir, - tail(importsToResolve) - )); - } + // eslint-disable-next-line no-shadow + function startResolving(dir, importsToResolve) { + return importsToResolve.length === 0 + ? Promise.reject() + : resolve(dir, importsToResolve[0]).then( + (resolvedFile) => { + // Add the resolvedFilename as dependency. Although we're also using stats.includedFiles, this might come + // in handy when an error occurs. In this case, we don't get stats.includedFiles from node-sass. + addNormalizedDependency(resolvedFile); + return { + // By removing the CSS file extension, we trigger node-sass to include the CSS file instead of just linking it. + file: resolvedFile.replace(matchCss, ''), + }; + }, + () => startResolving(dir, tail(importsToResolve)) + ); + } - return (url, prev, done) => { - startResolving( - dirContextFrom(prev), - importsToResolve(url) - ) - // Catch all resolving errors, return the original file and pass responsibility back to other custom importers - .catch(() => { return { file: url } }) - .then(done); - }; + return (url, prev, done) => { + startResolving(dirContextFrom(prev), importsToResolve(url)) + // Catch all resolving errors, return the original file and pass responsibility back to other custom importers + .catch(() => { + return { + file: url, + }; + }) + .then(done); + }; } module.exports = webpackImporter; diff --git a/package-lock.json b/package-lock.json index bedc61ba..ff47eb2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8825,6 +8825,12 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prettier": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.15.3.tgz", + "integrity": "sha512-gAU9AGAPMaKb3NNSUUuhhFAS7SCO4ALTN4nRIn6PJ075Qd28Yn2Ig2ahEJWdJwJmlEBTUfC7mMUSFy8MwsOCfg==", + "dev": true + }, "prettier-linter-helpers": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", diff --git a/package.json b/package.json index b2d64742..f64c0a59 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "node-sass": "^4.5.0", "nyc": "^13.1.0", "raw-loader": "^1.0.0", + "prettier": "^1.15.2", "sass": "^1.3.0", "should": "^13.2.3", "standard-version": "^4.2.0", diff --git a/test/bootstrapSass/webpack.config.js b/test/bootstrapSass/webpack.config.js index 26ca1561..4a58687a 100644 --- a/test/bootstrapSass/webpack.config.js +++ b/test/bootstrapSass/webpack.config.js @@ -1,36 +1,43 @@ -"use strict"; +'use strict'; -const path = require("path"); +const path = require('path'); -const sassLoader = require.resolve("../../lib/loader"); +const sassLoader = require.resolve('../../lib/loader'); module.exports = { - entry: path.resolve(__dirname, "../scss/bootstrap-sass.scss"), - output: { - path: path.resolve(__dirname, "../output"), - filename: "bundle.bootstrap-sass.js" - }, - devtool: "source-map", - module: { - rules: [{ - test: /\.scss$/, - use: [{ - loader: "style-loader" - }, { - loader: "css-loader" - }, { - loader: sassLoader, - options: { - includePaths: [ - path.resolve(__dirname, "../scss/includePath") - ] - } - }] - }, { - test: /\.woff2?$|\.ttf$|\.eot$|\.svg$/, - use: [{ - loader: "file-loader" - }] - }] - } + entry: path.resolve(__dirname, '../scss/bootstrap-sass.scss'), + output: { + path: path.resolve(__dirname, '../output'), + filename: 'bundle.bootstrap-sass.js', + }, + devtool: 'source-map', + module: { + rules: [ + { + test: /\.scss$/, + use: [ + { + loader: 'style-loader', + }, + { + loader: 'css-loader', + }, + { + loader: sassLoader, + options: { + includePaths: [path.resolve(__dirname, '../scss/includePath')], + }, + }, + ], + }, + { + test: /\.woff2?$|\.ttf$|\.eot$|\.svg$/, + use: [ + { + loader: 'file-loader', + }, + ], + }, + ], + }, }; diff --git a/test/extractText/webpack.config.js b/test/extractText/webpack.config.js index d42d7cfe..3bfc2e9f 100644 --- a/test/extractText/webpack.config.js +++ b/test/extractText/webpack.config.js @@ -1,37 +1,40 @@ -"use strict"; +'use strict'; -const path = require("path"); +const path = require('path'); // eslint-disable-next-line import/no-unresolved -const ExtractTextPlugin = require("extract-text-webpack-plugin"); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); -const sassLoader = require.resolve("../../lib/loader"); +const sassLoader = require.resolve('../../lib/loader'); const extractSass = new ExtractTextPlugin({ - filename: "[name].[contenthash].css", - disable: process.env.NODE_ENV === "development" + filename: '[name].[contenthash].css', + disable: process.env.NODE_ENV === 'development', }); module.exports = { - entry: require.resolve("../scss/language.scss"), - output: { - path: path.resolve(__dirname, "../output"), - filename: "bundle.extractText.js" - }, - module: { - rules: [{ - test: /\.scss$/, - loader: extractSass.extract({ - loader: [{ - loader: "css-loader" - }, { - loader: sassLoader - }], - fallbackLoader: "style-loader" - }) - }] - }, - plugins: [ - extractSass - ] + entry: require.resolve('../scss/language.scss'), + output: { + path: path.resolve(__dirname, '../output'), + filename: 'bundle.extractText.js', + }, + module: { + rules: [ + { + test: /\.scss$/, + loader: extractSass.extract({ + loader: [ + { + loader: 'css-loader', + }, + { + loader: sassLoader, + }, + ], + fallbackLoader: 'style-loader', + }), + }, + ], + }, + plugins: [extractSass], }; diff --git a/test/hmr/entry.js b/test/hmr/entry.js index c002fd74..0e1af7eb 100644 --- a/test/hmr/entry.js +++ b/test/hmr/entry.js @@ -1,9 +1,10 @@ -"use strict"; +'use strict'; /* global document */ -require("./simple.scss"); +require('./simple.scss'); setInterval(() => { - document.body.innerHTML += "
Now we just change the DOM, so that we can ensure that webpack is not just reloading the page"; + document.body.innerHTML += + '
Now we just change the DOM, so that we can ensure that webpack is not just reloading the page'; }, 2000); diff --git a/test/hmr/webpack.config.js b/test/hmr/webpack.config.js index b8c166ba..8d350ff5 100644 --- a/test/hmr/webpack.config.js +++ b/test/hmr/webpack.config.js @@ -1,25 +1,31 @@ -"use strict"; +'use strict'; -const path = require("path"); +const path = require('path'); -const sassLoader = require.resolve("../../lib/loader"); +const sassLoader = require.resolve('../../lib/loader'); module.exports = { - entry: path.resolve(__dirname, "./entry.js"), - output: { - path: path.resolve(__dirname, "../output"), - filename: "bundle.hmr.js" - }, - module: { - rules: [{ - test: /\.scss$/, - use: [{ - loader: "style-loader" - }, { - loader: "css-loader" - }, { - loader: sassLoader - }] - }] - } + entry: path.resolve(__dirname, './entry.js'), + output: { + path: path.resolve(__dirname, '../output'), + filename: 'bundle.hmr.js', + }, + module: { + rules: [ + { + test: /\.scss$/, + use: [ + { + loader: 'style-loader', + }, + { + loader: 'css-loader', + }, + { + loader: sassLoader, + }, + ], + }, + ], + }, }; diff --git a/test/index.test.js b/test/index.test.js index 67bc57c2..4bd5d43a 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -1,430 +1,579 @@ -"use strict"; +'use strict'; -require("should"); +require('should'); -const path = require("path"); +const path = require('path'); -const fs = require("fs"); +const fs = require('fs'); -const webpack = require("webpack"); -const merge = require("webpack-merge"); -const nodeSass = require("node-sass"); -const dartSass = require("sass"); -const mockRequire = require("mock-require"); +const webpack = require('webpack'); +const merge = require('webpack-merge'); +const nodeSass = require('node-sass'); +const dartSass = require('sass'); +const mockRequire = require('mock-require'); -const customImporter = require("./tools/customImporter.js"); -const customFunctions = require("./tools/customFunctions.js"); +const customImporter = require('./tools/customImporter.js'); +const customFunctions = require('./tools/customFunctions.js'); -const pathToSassLoader = require.resolve("../lib/loader.js"); -const testLoader = require("./tools/testLoader"); +const pathToSassLoader = require.resolve('../lib/loader.js'); +const testLoader = require('./tools/testLoader'); // eslint-disable-next-line import/no-dynamic-require const sassLoader = require(pathToSassLoader); const CR = /\r/g; const implementations = [nodeSass, dartSass]; -const syntaxStyles = ["scss", "sass"]; -const pathToErrorFileNotFound = path.resolve(__dirname, "./scss/error-file-not-found.scss"); -const pathToErrorFileNotFound2 = path.resolve(__dirname, "./scss/error-file-not-found-2.scss"); -const pathToErrorFile = path.resolve(__dirname, "./scss/error.scss"); -const pathToErrorImport = path.resolve(__dirname, "./scss/error-import.scss"); +const syntaxStyles = ['scss', 'sass']; +const pathToErrorFileNotFound = path.resolve( + __dirname, + './scss/error-file-not-found.scss' +); +const pathToErrorFileNotFound2 = path.resolve( + __dirname, + './scss/error-file-not-found-2.scss' +); +const pathToErrorFile = path.resolve(__dirname, './scss/error.scss'); +const pathToErrorImport = path.resolve(__dirname, './scss/error-import.scss'); const loaderContextMock = { - async: Function.prototype, - cacheable: Function.prototype, - dependency: Function.prototype + async: Function.prototype, + cacheable: Function.prototype, + dependency: Function.prototype, }; -Object.defineProperty(loaderContextMock, "options", { - set() { }, - get() { - throw new Error("webpack options are not allowed to be accessed anymore."); - } +Object.defineProperty(loaderContextMock, 'options', { + set() {}, + get() { + throw new Error('webpack options are not allowed to be accessed anymore.'); + }, }); -implementations.forEach(implementation => { - const [implementationName] = implementation.info.split("\t"); - - describe(implementationName, () => { - syntaxStyles.forEach(ext => { - function execTest(testId, loaderOptions, webpackOptions) { - const bundleName = `bundle.${ ext }.${ implementationName }.js`; - - return new Promise((resolve, reject) => { - const baseConfig = merge({ - entry: path.join(__dirname, ext, `${ testId }.${ ext }`), - output: { - filename: bundleName - } - }, webpackOptions); - - runWebpack(baseConfig, loaderOptions, (err) => err ? reject(err) : resolve()); - }).then(() => { - const actualCss = readBundle(bundleName); - const expectedCss = readCss(ext, testId); - - // writing the actual css to output-dir for better debugging - // fs.writeFileSync(path.join(__dirname, "output", `${ testId }.${ ext }.css`), actualCss, "utf8"); - actualCss.should.eql(expectedCss); - }); - } - - describe(`sass-loader (${ ext })`, () => { - describe("basic", () => { - it("should compile simple sass without errors", () => execTest("language")); - }); - describe("imports", () => { - it("should resolve imports correctly", () => execTest("imports")); - // Test for issue: https://github.com/webpack-contrib/sass-loader/issues/32 - it("should pass with multiple imports", () => execTest("multiple-imports")); - // Test for issue: https://github.com/webpack-contrib/sass-loader/issues/73 - it("should resolve imports from other language style correctly", () => execTest("import-other-style")); - // Test for includePath imports - it("should resolve imports from another directory declared by includePaths correctly", () => execTest("import-include-paths", { - includePaths: [path.join(__dirname, ext, "includePath")] - })); - // Legacy support for CSS imports with node-sass - // See discussion https://github.com/webpack-contrib/sass-loader/pull/573/files?#r199109203 - if (implementation === nodeSass) { - it("should not resolve CSS imports", () => execTest("import-css")); - } - it("should compile bootstrap-sass without errors", () => execTest("bootstrap-sass")); - it("should correctly import scoped npm packages", () => execTest("import-from-npm-org-pkg")); - it("should resolve aliases", () => execTest("import-alias", {}, { - resolve: { - alias: { - "path-to-alias": path.join(__dirname, ext, "another", `alias.${ ext }`) - } - } - })); - }); - describe("custom importers", () => { - it("should use custom importer", () => execTest("custom-importer", { - importer: customImporter - })); - }); - describe("custom functions", () => { - it("should expose custom functions", () => execTest("custom-functions", { - functions: customFunctions(implementation) - })); - }); - describe("prepending data", () => { - it("should extend the data-option if present", () => execTest("prepending-data", { - data: `$prepended-data: hotpink${ ext === "sass" ? "\n" : ";" }` - })); - }); - // See https://github.com/webpack-contrib/sass-loader/issues/21 - describe("empty files", () => { - it("should compile without errors", () => execTest("empty")); - }); - }); +implementations.forEach((implementation) => { + const [implementationName] = implementation.info.split('\t'); + + describe(implementationName, () => { + syntaxStyles.forEach((ext) => { + function execTest(testId, loaderOptions, webpackOptions) { + const bundleName = `bundle.${ext}.${implementationName}.js`; + + return new Promise((resolve, reject) => { + const baseConfig = merge( + { + entry: path.join(__dirname, ext, `${testId}.${ext}`), + output: { + filename: bundleName, + }, + }, + webpackOptions + ); + + runWebpack(baseConfig, loaderOptions, (err) => + err ? reject(err) : resolve() + ); + }).then(() => { + const actualCss = readBundle(bundleName); + const expectedCss = readCss(ext, testId); + + // writing the actual css to output-dir for better debugging + // fs.writeFileSync(path.join(__dirname, "output", `${ testId }.${ ext }.css`), actualCss, "utf8"); + actualCss.should.eql(expectedCss); }); + } - describe("sass-loader", () => { - describe("multiple compilations", () => { - it("should not interfere with each other", () => - new Promise((resolve, reject) => { - runWebpack({ - entry: { - b: path.join(__dirname, "scss", "multipleCompilations", "b.scss"), - c: path.join(__dirname, "scss", "multipleCompilations", "c.scss"), - a: path.join(__dirname, "scss", "multipleCompilations", "a.scss"), - d: path.join(__dirname, "scss", "multipleCompilations", "d.scss"), - e: path.join(__dirname, "scss", "multipleCompilations", "e.scss"), - f: path.join(__dirname, "scss", "multipleCompilations", "f.scss"), - g: path.join(__dirname, "scss", "multipleCompilations", "g.scss"), - h: path.join(__dirname, "scss", "multipleCompilations", "h.scss") - }, - output: { - filename: "bundle.multiple-compilations.[name].js" - } - }, {}, err => err ? reject(err) : resolve()); - }) - .then(() => { - const expectedCss = readCss("scss", "imports"); - const a = readBundle("bundle.multiple-compilations.a.js"); - const b = readBundle("bundle.multiple-compilations.b.js"); - const c = readBundle("bundle.multiple-compilations.c.js"); - const d = readBundle("bundle.multiple-compilations.d.js"); - const e = readBundle("bundle.multiple-compilations.e.js"); - const f = readBundle("bundle.multiple-compilations.f.js"); - const g = readBundle("bundle.multiple-compilations.g.js"); - const h = readBundle("bundle.multiple-compilations.h.js"); - - a.should.equal(expectedCss); - b.should.equal(expectedCss); - c.should.equal(expectedCss); - d.should.equal(expectedCss); - e.should.equal(expectedCss); - f.should.equal(expectedCss); - g.should.equal(expectedCss); - h.should.equal(expectedCss); - }) - ); - }); - describe("source maps", () => { - function buildWithSourceMaps() { - return new Promise((resolve, reject) => { - webpack({ - entry: path.join(__dirname, "scss", "imports.scss"), - mode: "development", - output: { - path: path.join(__dirname, "output"), - filename: "bundle.source-maps.js", - libraryTarget: "commonjs2" - }, - devtool: "source-map", - module: { - rules: [{ - test: /\.scss$/, - use: [ - { loader: testLoader.filename }, - { - loader: pathToSassLoader, options: { - implementation, - sourceMap: true - } - } - ] - }] - } - }, (webpackErr, stats) => { - const err = webpackErr || - (stats.hasErrors() && stats.compilation.errors[0]) || - (stats.hasWarnings() && stats.compilation.warnings[0]); - - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); - } - - it("should compile without errors", () => buildWithSourceMaps()); - it("should produce a valid source map", () => { - const cwdGetter = process.cwd; - const fakeCwd = path.join(__dirname, "scss"); - - process.cwd = () => fakeCwd; - - return buildWithSourceMaps() - .then(() => { - const { sourceMap } = testLoader; - - sourceMap.should.not.have.property("file"); - sourceMap.should.have.property("sourceRoot", fakeCwd); - // This number needs to be updated if imports.scss or any dependency of that changes. - // Node Sass includes a duplicate entry, Dart Sass does not. - sourceMap.sources.should.have.length(implementation === nodeSass ? 11 : 10); - sourceMap.sources.forEach(sourcePath => - fs.existsSync(path.resolve(sourceMap.sourceRoot, sourcePath)) - ); - - process.cwd = cwdGetter; - }); - }); - }); - describe("errors", () => { - it("should throw an error in synchronous loader environments", () => { - try { - sassLoader.call(Object.create(loaderContextMock), ""); - } catch (err) { - // check for file excerpt - err.message.should.equal("Synchronous compilation is not supported anymore. See https://github.com/webpack-contrib/sass-loader/issues/333"); - } - }); - it("should output understandable errors in entry files", (done) => { - runWebpack({ - entry: pathToErrorFile - }, {}, (err) => { - if (implementation === nodeSass) { - err.message.should.match(/Property "some-value" must be followed by a ':'/); - err.message.should.match(/\(line 2, column 5\)/); - } else { - err.message.should.match(/Expected "{"./); - err.message.should.match(/\(line 3, column 1\)/); - } - err.message.indexOf(pathToErrorFile).should.not.equal(-1); - done(); - }); - }); - it("should output understandable errors of imported files", (done) => { - runWebpack({ - entry: pathToErrorImport - }, {}, (err) => { - // check for file excerpt - if (implementation === nodeSass) { - err.message.should.match(/Property "some-value" must be followed by a ':'/); - err.message.should.match(/\(line 2, column 5\)/); - } else { - err.message.should.match(/Expected "{"./); - err.message.should.match(/\(line 3, column 1\)/); - } - err.message.indexOf(pathToErrorFile).should.not.equal(-1); - done(); - }); - }); - it("should output understandable errors when a file could not be found", (done) => { - runWebpack({ - entry: pathToErrorFileNotFound - }, {}, (err) => { - err.message.should.match(/@import "does-not-exist";/); - if (implementation === nodeSass) { - err.message.should.match(/File to import not found or unreadable: does-not-exist/); - err.message.should.match(/\(line 1, column 1\)/); - } else { - err.message.should.match(/Can't find stylesheet to import\./); - err.message.should.match(/\(line 1, column 9\)/); - } - err.message.indexOf(pathToErrorFileNotFound).should.not.equal(-1); - done(); - }); - }); - it("should not auto-resolve imports with explicit file names", (done) => { - runWebpack({ - entry: pathToErrorFileNotFound2 - }, {}, (err) => { - err.message.should.match(/@import "\.\/another\/_module\.scss";/); - if (implementation === nodeSass) { - err.message.should.match(/File to import not found or unreadable: \.\/another\/_module\.scss/); - err.message.should.match(/\(line 1, column 1\)/); - } else { - err.message.should.match(/Can't find stylesheet to import\./); - err.message.should.match(/\(line 1, column 9\)/); - } - err.message.indexOf(pathToErrorFileNotFound2).should.not.equal(-1); - done(); - }); - }); - it("should not swallow errors when trying to load node-sass", (done) => { - mockRequire.reRequire(pathToSassLoader); - // eslint-disable-next-line global-require - const module = require("module"); - // eslint-disable-next-line no-underscore-dangle - const originalResolve = module._resolveFilename; - - // eslint-disable-next-line no-underscore-dangle - module._resolveFilename = function _resolveFilename(filename) { - if (!filename.match(/node-sass/)) { - // eslint-disable-next-line prefer-rest-params - return originalResolve.apply(this, arguments); - } - - const err = new Error("Some error"); - - err.code = "MODULE_NOT_FOUND"; - - throw err; - }; - runWebpack({ - entry: `${ pathToSassLoader }!${ pathToErrorFile }` - }, { implementation: null }, (err) => { - // eslint-disable-next-line no-underscore-dangle - module._resolveFilename = originalResolve; - mockRequire.reRequire("node-sass"); - err.message.should.match(/Some error/); - done(); - }); - }); - it("should output a message when the Sass info is unparseable", (done) => { - mockRequire.reRequire(pathToSassLoader); - runWebpack({ - entry: pathToErrorFile - }, { - implementation: merge(nodeSass, { info: "asdfj" }) - }, (err) => { - err.message.should.match(/Unknown Sass implementation "asdfj"\./); - done(); - }); - }); - it("should output a message when the Sass version is unparseable", (done) => { - mockRequire.reRequire(pathToSassLoader); - runWebpack({ - entry: pathToErrorFile - }, { - implementation: merge(nodeSass, { info: "node-sass\t1" }) - }, (err) => { - err.message.should.match(/Invalid Sass version "1"\./); - done(); - }); - }); - it("should output a message when Node Sass is an incompatible version", (done) => { - mockRequire.reRequire(pathToSassLoader); - runWebpack({ - entry: pathToErrorFile - }, { - implementation: merge(nodeSass, { info: "node-sass\t3.0.0" }) - }, (err) => { - err.message.should.match(/Node Sass version 3\.0\.0 is incompatible with \^4\.0\.0\./); - done(); - }); - }); - it("should output a message when Dart Sass is an incompatible version", (done) => { - mockRequire.reRequire(pathToSassLoader); - runWebpack({ - entry: pathToErrorFile - }, { - implementation: merge(nodeSass, { info: "dart-sass\t1.2.0" }) - }, (err) => { - err.message.should.match(/Dart Sass version 1\.2\.0 is incompatible with \^1\.3\.0\./); - done(); - }); - }); - it("should output a message for an unknown sass implementation", (done) => { - mockRequire.reRequire(pathToSassLoader); - runWebpack({ - entry: pathToErrorFile - }, { - implementation: merge(nodeSass, { info: "strange-sass\t1.0.0" }) - }, (err) => { - err.message.should.match(/Unknown Sass implementation "strange-sass"\./); - done(); - }); - }); - }); + describe(`sass-loader (${ext})`, () => { + describe('basic', () => { + it('should compile simple sass without errors', () => + execTest('language')); }); + describe('imports', () => { + it('should resolve imports correctly', () => execTest('imports')); + // Test for issue: https://github.com/webpack-contrib/sass-loader/issues/32 + it('should pass with multiple imports', () => + execTest('multiple-imports')); + // Test for issue: https://github.com/webpack-contrib/sass-loader/issues/73 + it('should resolve imports from other language style correctly', () => + execTest('import-other-style')); + // Test for includePath imports + it('should resolve imports from another directory declared by includePaths correctly', () => + execTest('import-include-paths', { + includePaths: [path.join(__dirname, ext, 'includePath')], + })); + // Legacy support for CSS imports with node-sass + // See discussion https://github.com/webpack-contrib/sass-loader/pull/573/files?#r199109203 + if (implementation === nodeSass) { + it('should not resolve CSS imports', () => execTest('import-css')); + } + it('should compile bootstrap-sass without errors', () => + execTest('bootstrap-sass')); + it('should correctly import scoped npm packages', () => + execTest('import-from-npm-org-pkg')); + it('should resolve aliases', () => + execTest( + 'import-alias', + {}, + { + resolve: { + alias: { + 'path-to-alias': path.join( + __dirname, + ext, + 'another', + `alias.${ext}` + ), + }, + }, + } + )); + }); + describe('custom importers', () => { + it('should use custom importer', () => + execTest('custom-importer', { + importer: customImporter, + })); + }); + describe('custom functions', () => { + it('should expose custom functions', () => + execTest('custom-functions', { + functions: customFunctions(implementation), + })); + }); + describe('prepending data', () => { + it('should extend the data-option if present', () => + execTest('prepending-data', { + data: `$prepended-data: hotpink${ext === 'sass' ? '\n' : ';'}`, + })); + }); + // See https://github.com/webpack-contrib/sass-loader/issues/21 + describe('empty files', () => { + it('should compile without errors', () => execTest('empty')); + }); + }); + }); - function readCss(ext, id) { - return fs.readFileSync(path.join(__dirname, ext, "spec", implementationName, `${ id }.css`), "utf8").replace(CR, ""); - } - - function runWebpack(baseConfig, loaderOptions, done) { - const webpackConfig = merge({ - mode: "development", + describe('sass-loader', () => { + describe('multiple compilations', () => { + it('should not interfere with each other', () => + new Promise((resolve, reject) => { + runWebpack( + { + entry: { + b: path.join( + __dirname, + 'scss', + 'multipleCompilations', + 'b.scss' + ), + c: path.join( + __dirname, + 'scss', + 'multipleCompilations', + 'c.scss' + ), + a: path.join( + __dirname, + 'scss', + 'multipleCompilations', + 'a.scss' + ), + d: path.join( + __dirname, + 'scss', + 'multipleCompilations', + 'd.scss' + ), + e: path.join( + __dirname, + 'scss', + 'multipleCompilations', + 'e.scss' + ), + f: path.join( + __dirname, + 'scss', + 'multipleCompilations', + 'f.scss' + ), + g: path.join( + __dirname, + 'scss', + 'multipleCompilations', + 'g.scss' + ), + h: path.join( + __dirname, + 'scss', + 'multipleCompilations', + 'h.scss' + ), + }, output: { - path: path.join(__dirname, "output"), - filename: "bundle.js", - libraryTarget: "commonjs2" + filename: 'bundle.multiple-compilations.[name].js', }, + }, + {}, + (err) => (err ? reject(err) : resolve()) + ); + }).then(() => { + const expectedCss = readCss('scss', 'imports'); + const a = readBundle('bundle.multiple-compilations.a.js'); + const b = readBundle('bundle.multiple-compilations.b.js'); + const c = readBundle('bundle.multiple-compilations.c.js'); + const d = readBundle('bundle.multiple-compilations.d.js'); + const e = readBundle('bundle.multiple-compilations.e.js'); + const f = readBundle('bundle.multiple-compilations.f.js'); + const g = readBundle('bundle.multiple-compilations.g.js'); + const h = readBundle('bundle.multiple-compilations.h.js'); + + a.should.equal(expectedCss); + b.should.equal(expectedCss); + c.should.equal(expectedCss); + d.should.equal(expectedCss); + e.should.equal(expectedCss); + f.should.equal(expectedCss); + g.should.equal(expectedCss); + h.should.equal(expectedCss); + })); + }); + describe('source maps', () => { + function buildWithSourceMaps() { + return new Promise((resolve, reject) => { + webpack( + { + entry: path.join(__dirname, 'scss', 'imports.scss'), + mode: 'development', + output: { + path: path.join(__dirname, 'output'), + filename: 'bundle.source-maps.js', + libraryTarget: 'commonjs2', + }, + devtool: 'source-map', module: { - rules: [{ - test: /\.s[ac]ss$/, - use: [ - { loader: "raw-loader" }, - { - loader: pathToSassLoader, - options: merge({ implementation }, loaderOptions) - } - ] - }] + rules: [ + { + test: /\.scss$/, + use: [ + { loader: testLoader.filename }, + { + loader: pathToSassLoader, + options: { + implementation, + sourceMap: true, + }, + }, + ], + }, + ], + }, + }, + (webpackErr, stats) => { + const err = + webpackErr || + (stats.hasErrors() && stats.compilation.errors[0]) || + (stats.hasWarnings() && stats.compilation.warnings[0]); + + if (err) { + reject(err); + } else { + resolve(); } - }, baseConfig); + } + ); + }); + } - webpack(webpackConfig, (webpackErr, stats) => { - const err = webpackErr || - (stats.hasErrors() && stats.compilation.errors[0]) || - (stats.hasWarnings() && stats.compilation.warnings[0]); + it('should compile without errors', () => buildWithSourceMaps()); + it('should produce a valid source map', () => { + const cwdGetter = process.cwd; + const fakeCwd = path.join(__dirname, 'scss'); + + process.cwd = () => fakeCwd; + + return buildWithSourceMaps().then(() => { + const { sourceMap } = testLoader; + + sourceMap.should.not.have.property('file'); + sourceMap.should.have.property('sourceRoot', fakeCwd); + // This number needs to be updated if imports.scss or any dependency of that changes. + // Node Sass includes a duplicate entry, Dart Sass does not. + sourceMap.sources.should.have.length( + implementation === nodeSass ? 11 : 10 + ); + sourceMap.sources.forEach((sourcePath) => + fs.existsSync(path.resolve(sourceMap.sourceRoot, sourcePath)) + ); + + process.cwd = cwdGetter; + }); + }); + }); + describe('errors', () => { + it('should throw an error in synchronous loader environments', () => { + try { + sassLoader.call(Object.create(loaderContextMock), ''); + } catch (err) { + // check for file excerpt + err.message.should.equal( + 'Synchronous compilation is not supported anymore. See https://github.com/webpack-contrib/sass-loader/issues/333' + ); + } + }); + it('should output understandable errors in entry files', (done) => { + runWebpack( + { + entry: pathToErrorFile, + }, + {}, + (err) => { + if (implementation === nodeSass) { + err.message.should.match( + /Property "some-value" must be followed by a ':'/ + ); + err.message.should.match(/\(line 2, column 5\)/); + } else { + err.message.should.match(/Expected "{"./); + err.message.should.match(/\(line 3, column 1\)/); + } + err.message.indexOf(pathToErrorFile).should.not.equal(-1); + done(); + } + ); + }); + it('should output understandable errors of imported files', (done) => { + runWebpack( + { + entry: pathToErrorImport, + }, + {}, + (err) => { + // check for file excerpt + if (implementation === nodeSass) { + err.message.should.match( + /Property "some-value" must be followed by a ':'/ + ); + err.message.should.match(/\(line 2, column 5\)/); + } else { + err.message.should.match(/Expected "{"./); + err.message.should.match(/\(line 3, column 1\)/); + } + err.message.indexOf(pathToErrorFile).should.not.equal(-1); + done(); + } + ); + }); + it('should output understandable errors when a file could not be found', (done) => { + runWebpack( + { + entry: pathToErrorFileNotFound, + }, + {}, + (err) => { + err.message.should.match(/@import "does-not-exist";/); + if (implementation === nodeSass) { + err.message.should.match( + /File to import not found or unreadable: does-not-exist/ + ); + err.message.should.match(/\(line 1, column 1\)/); + } else { + err.message.should.match(/Can't find stylesheet to import\./); + err.message.should.match(/\(line 1, column 9\)/); + } + err.message.indexOf(pathToErrorFileNotFound).should.not.equal(-1); + done(); + } + ); + }); + it('should not auto-resolve imports with explicit file names', (done) => { + runWebpack( + { + entry: pathToErrorFileNotFound2, + }, + {}, + (err) => { + err.message.should.match(/@import "\.\/another\/_module\.scss";/); + if (implementation === nodeSass) { + err.message.should.match( + /File to import not found or unreadable: \.\/another\/_module\.scss/ + ); + err.message.should.match(/\(line 1, column 1\)/); + } else { + err.message.should.match(/Can't find stylesheet to import\./); + err.message.should.match(/\(line 1, column 9\)/); + } + err.message + .indexOf(pathToErrorFileNotFound2) + .should.not.equal(-1); + done(); + } + ); + }); + it('should not swallow errors when trying to load node-sass', (done) => { + mockRequire.reRequire(pathToSassLoader); + // eslint-disable-next-line global-require + const module = require('module'); + // eslint-disable-next-line no-underscore-dangle + const originalResolve = module._resolveFilename; + + // eslint-disable-next-line no-underscore-dangle + module._resolveFilename = function _resolveFilename(filename) { + if (!filename.match(/node-sass/)) { + // eslint-disable-next-line prefer-rest-params + return originalResolve.apply(this, arguments); + } - done(err || null); - }); - } + const err = new Error('Some error'); + + err.code = 'MODULE_NOT_FOUND'; + + throw err; + }; + runWebpack( + { + entry: `${pathToSassLoader}!${pathToErrorFile}`, + }, + { implementation: null }, + (err) => { + // eslint-disable-next-line no-underscore-dangle + module._resolveFilename = originalResolve; + mockRequire.reRequire('node-sass'); + err.message.should.match(/Some error/); + done(); + } + ); + }); + it('should output a message when the Sass info is unparseable', (done) => { + mockRequire.reRequire(pathToSassLoader); + runWebpack( + { + entry: pathToErrorFile, + }, + { + implementation: merge(nodeSass, { info: 'asdfj' }), + }, + (err) => { + err.message.should.match(/Unknown Sass implementation "asdfj"\./); + done(); + } + ); + }); + it('should output a message when the Sass version is unparseable', (done) => { + mockRequire.reRequire(pathToSassLoader); + runWebpack( + { + entry: pathToErrorFile, + }, + { + implementation: merge(nodeSass, { info: 'node-sass\t1' }), + }, + (err) => { + err.message.should.match(/Invalid Sass version "1"\./); + done(); + } + ); + }); + it('should output a message when Node Sass is an incompatible version', (done) => { + mockRequire.reRequire(pathToSassLoader); + runWebpack( + { + entry: pathToErrorFile, + }, + { + implementation: merge(nodeSass, { info: 'node-sass\t3.0.0' }), + }, + (err) => { + err.message.should.match( + /Node Sass version 3\.0\.0 is incompatible with \^4\.0\.0\./ + ); + done(); + } + ); + }); + it('should output a message when Dart Sass is an incompatible version', (done) => { + mockRequire.reRequire(pathToSassLoader); + runWebpack( + { + entry: pathToErrorFile, + }, + { + implementation: merge(nodeSass, { info: 'dart-sass\t1.2.0' }), + }, + (err) => { + err.message.should.match( + /Dart Sass version 1\.2\.0 is incompatible with \^1\.3\.0\./ + ); + done(); + } + ); + }); + it('should output a message for an unknown sass implementation', (done) => { + mockRequire.reRequire(pathToSassLoader); + runWebpack( + { + entry: pathToErrorFile, + }, + { + implementation: merge(nodeSass, { info: 'strange-sass\t1.0.0' }), + }, + (err) => { + err.message.should.match( + /Unknown Sass implementation "strange-sass"\./ + ); + done(); + } + ); + }); + }); }); + + function readCss(ext, id) { + return fs + .readFileSync( + path.join(__dirname, ext, 'spec', implementationName, `${id}.css`), + 'utf8' + ) + .replace(CR, ''); + } + + function runWebpack(baseConfig, loaderOptions, done) { + const webpackConfig = merge( + { + mode: 'development', + output: { + path: path.join(__dirname, 'output'), + filename: 'bundle.js', + libraryTarget: 'commonjs2', + }, + module: { + rules: [ + { + test: /\.s[ac]ss$/, + use: [ + { loader: 'raw-loader' }, + { + loader: pathToSassLoader, + options: merge({ implementation }, loaderOptions), + }, + ], + }, + ], + }, + }, + baseConfig + ); + + webpack(webpackConfig, (webpackErr, stats) => { + const err = + webpackErr || + (stats.hasErrors() && stats.compilation.errors[0]) || + (stats.hasWarnings() && stats.compilation.warnings[0]); + + done(err || null); + }); + } + }); }); function readBundle(filename) { - delete require.cache[path.resolve(__dirname, `./output/${ filename }`)]; + delete require.cache[path.resolve(__dirname, `./output/${filename}`)]; - // eslint-disable-next-line global-require, import/no-dynamic-require - return require(`./output/${ filename }`); + // eslint-disable-next-line global-require, import/no-dynamic-require + return require(`./output/${filename}`); } diff --git a/test/scss/another/module.js b/test/scss/another/module.js index c04a56e3..2b4811fe 100644 --- a/test/scss/another/module.js +++ b/test/scss/another/module.js @@ -1,4 +1,4 @@ // ./another/module.js: The sass-loader should not try to import that. scss, sass and css extensions should be preferred. // See https://github.com/webpack-contrib/sass-loader/issues/556#issuecomment-381154009 -"This should not be imported"; +'This should not be imported'; diff --git a/test/sourceMap/webpack.config.js b/test/sourceMap/webpack.config.js index bf7fb3c2..86272ce3 100644 --- a/test/sourceMap/webpack.config.js +++ b/test/sourceMap/webpack.config.js @@ -1,29 +1,37 @@ -"use strict"; +'use strict'; -const path = require("path"); +const path = require('path'); -const sassLoader = require.resolve("../../lib/loader"); +const sassLoader = require.resolve('../../lib/loader'); module.exports = { - entry: path.resolve(__dirname, "..", "scss", "imports.scss"), - output: { - filename: "bundle.js" - }, - devtool: "source-map", - module: { - rules: [{ - test: /\.scss$/, - use: [{ - loader: "style-loader" - }, { - loader: "css-loader", options: { - sourceMap: true - } - }, { - loader: sassLoader, options: { - sourceMap: true - } - }] - }] - } + entry: path.resolve(__dirname, '..', 'scss', 'imports.scss'), + output: { + filename: 'bundle.js', + }, + devtool: 'source-map', + module: { + rules: [ + { + test: /\.scss$/, + use: [ + { + loader: 'style-loader', + }, + { + loader: 'css-loader', + options: { + sourceMap: true, + }, + }, + { + loader: sassLoader, + options: { + sourceMap: true, + }, + }, + ], + }, + ], + }, }; diff --git a/test/spec.test.js b/test/spec.test.js index eccbd746..ef3d4e1d 100644 --- a/test/spec.test.js +++ b/test/spec.test.js @@ -1,4 +1,4 @@ -"use strict"; +'use strict'; /** * This file can be used to track changes in the spec introduced by newer node-sass versions. @@ -13,54 +13,51 @@ * 5. Run `npm run test` */ -require("should"); -const fs = require("fs"); -const path = require("path"); +require('should'); +const fs = require('fs'); +const path = require('path'); -const createSpec = require("./tools/createSpec.js"); +const createSpec = require('./tools/createSpec.js'); const testFolder = __dirname; const matchCss = /\.css$/; function readSpec(folder) { - const result = {}; + const result = {}; - fs.readdirSync(folder) - .forEach((file) => { - if (matchCss.test(file)) { - result[file] = fs.readFileSync(path.join(folder, file), "utf8"); - } - }); + fs.readdirSync(folder).forEach((file) => { + if (matchCss.test(file)) { + result[file] = fs.readFileSync(path.join(folder, file), 'utf8'); + } + }); - return result; + return result; } function writeSpec(folder, spec) { - Object.keys(spec) - .forEach((specName) => { - fs.writeFileSync(path.resolve(folder, specName), spec[specName], "utf8"); - }); + Object.keys(spec).forEach((specName) => { + fs.writeFileSync(path.resolve(folder, specName), spec[specName], 'utf8'); + }); } -["scss", "sass"].forEach((ext) => { - describe(`${ext} spec`, () => { - const specFolder = path.resolve(testFolder, ext, "spec"); - const oldSpec = readSpec(specFolder); +['scss', 'sass'].forEach((ext) => { + describe(`${ext} spec`, () => { + const specFolder = path.resolve(testFolder, ext, 'spec'); + const oldSpec = readSpec(specFolder); - createSpec(ext); + createSpec(ext); - const newSpec = readSpec(specFolder); + const newSpec = readSpec(specFolder); - Object.keys(oldSpec) - .forEach((specName) => { - it(`${specName} should not have been changed`, () => { - oldSpec[specName].should.eql(newSpec[specName]); - }); - }); + Object.keys(oldSpec).forEach((specName) => { + it(`${specName} should not have been changed`, () => { + oldSpec[specName].should.eql(newSpec[specName]); + }); + }); - after(() => { - // Write old spec back to the folder so that future tests will also fail - writeSpec(specFolder, oldSpec); - }); + after(() => { + // Write old spec back to the folder so that future tests will also fail + writeSpec(specFolder, oldSpec); }); + }); }); diff --git a/test/tools/createSpec.js b/test/tools/createSpec.js index b6de8245..2361d6be 100644 --- a/test/tools/createSpec.js +++ b/test/tools/createSpec.js @@ -1,85 +1,108 @@ -"use strict"; +'use strict'; -const fs = require("fs"); -const path = require("path"); -const os = require("os"); +const fs = require('fs'); +const path = require('path'); +const os = require('os'); -const dartSass = require("sass"); -const nodeSass = require("node-sass"); +const dartSass = require('sass'); +const nodeSass = require('node-sass'); -const customImporter = require("./customImporter.js"); -const customFunctions = require("./customFunctions.js"); +const customImporter = require('./customImporter.js'); +const customFunctions = require('./customFunctions.js'); const implementations = [nodeSass, dartSass]; -const testFolder = path.resolve(__dirname, "../"); -const error = "error"; +const testFolder = path.resolve(__dirname, '../'); +const error = 'error'; function createSpec(ext) { - const basePath = path.join(testFolder, ext); - const testNodeModules = path.relative(basePath, path.join(testFolder, "node_modules")) + path.sep; - const pathToBootstrap = path.relative(basePath, path.resolve(testFolder, "..", "node_modules", "bootstrap-sass")); - const pathToScopedNpmPkg = path.relative(basePath, path.resolve(testFolder, "node_modules", "@org", "pkg", "./index.scss")); - const pathToModule = path.relative(basePath, path.resolve(testFolder, "node_modules", "module", "module.scss")); - const pathToAnother = path.relative(basePath, path.resolve(testFolder, "node_modules", "another", "module.scss")); - const pathToFooAlias = path.relative(basePath, path.resolve(testFolder, ext, "another", `alias.${ ext}`)); + const basePath = path.join(testFolder, ext); + const testNodeModules = + path.relative(basePath, path.join(testFolder, 'node_modules')) + path.sep; + const pathToBootstrap = path.relative( + basePath, + path.resolve(testFolder, '..', 'node_modules', 'bootstrap-sass') + ); + const pathToScopedNpmPkg = path.relative( + basePath, + path.resolve(testFolder, 'node_modules', '@org', 'pkg', './index.scss') + ); + const pathToModule = path.relative( + basePath, + path.resolve(testFolder, 'node_modules', 'module', 'module.scss') + ); + const pathToAnother = path.relative( + basePath, + path.resolve(testFolder, 'node_modules', 'another', 'module.scss') + ); + const pathToFooAlias = path.relative( + basePath, + path.resolve(testFolder, ext, 'another', `alias.${ext}`) + ); - fs.readdirSync(path.join(testFolder, ext)) - .filter( - (file) => path.extname(file) === `.${ ext}` && file.slice(0, error.length) !== error - ) - .forEach((file) => { - const fileName = path.join(basePath, file); - const fileWithoutExt = file.slice(0, -ext.length - 1); - const sassOptions = { - importer(url) { - if (url === "import-with-custom-logic") { - return customImporter.returnValue; - } - // Do not transform css imports - if (/\.css$/.test(url) === false) { - // eslint-disable-next-line no-param-reassign - url = url - .replace(/^~bootstrap-sass/, pathToBootstrap) - .replace(/^~@org\/pkg/, pathToScopedNpmPkg) - .replace(/^~module/, pathToModule) - .replace(/^~another/, pathToAnother) - .replace(/^~/, testNodeModules) - .replace(/^path-to-alias/, pathToFooAlias); - } - return { - file: url - }; - }, - includePaths: [ - path.join(testFolder, ext, "another"), - path.join(testFolder, ext, "includePath") - ] - }; + fs.readdirSync(path.join(testFolder, ext)) + .filter( + (file) => + path.extname(file) === `.${ext}` && + file.slice(0, error.length) !== error + ) + .forEach((file) => { + const fileName = path.join(basePath, file); + const fileWithoutExt = file.slice(0, -ext.length - 1); + const sassOptions = { + importer(url) { + if (url === 'import-with-custom-logic') { + return customImporter.returnValue; + } + // Do not transform css imports + if (/\.css$/.test(url) === false) { + // eslint-disable-next-line no-param-reassign + url = url + .replace(/^~bootstrap-sass/, pathToBootstrap) + .replace(/^~@org\/pkg/, pathToScopedNpmPkg) + .replace(/^~module/, pathToModule) + .replace(/^~another/, pathToAnother) + .replace(/^~/, testNodeModules) + .replace(/^path-to-alias/, pathToFooAlias); + } + return { + file: url, + }; + }, + includePaths: [ + path.join(testFolder, ext, 'another'), + path.join(testFolder, ext, 'includePath'), + ], + }; - if (/prepending-data/.test(fileName)) { - sassOptions.indentedSyntax = /\.sass$/.test(fileName); - sassOptions.data = `$prepended-data: hotpink${ sassOptions.indentedSyntax ? "\n" : ";" - }${os.EOL }${fs.readFileSync(fileName, "utf8")}`; - } else { - sassOptions.file = fileName; - } + if (/prepending-data/.test(fileName)) { + sassOptions.indentedSyntax = /\.sass$/.test(fileName); + sassOptions.data = `$prepended-data: hotpink${ + sassOptions.indentedSyntax ? '\n' : ';' + }${os.EOL}${fs.readFileSync(fileName, 'utf8')}`; + } else { + sassOptions.file = fileName; + } - implementations.forEach(implementation => { - if (fileWithoutExt === "import-css" && implementation !== nodeSass) { - // Skip CSS imports for all implementations that are not node-sass - // CSS imports is a legacy feature that we only support for node-sass - // See discussion https://github.com/webpack-contrib/sass-loader/pull/573/files?#r199109203 - return; - } + implementations.forEach((implementation) => { + if (fileWithoutExt === 'import-css' && implementation !== nodeSass) { + // Skip CSS imports for all implementations that are not node-sass + // CSS imports is a legacy feature that we only support for node-sass + // See discussion https://github.com/webpack-contrib/sass-loader/pull/573/files?#r199109203 + return; + } - sassOptions.functions = customFunctions(implementation); + sassOptions.functions = customFunctions(implementation); - const [name] = implementation.info.split("\t"); - const { css } = implementation.renderSync(sassOptions); + const [name] = implementation.info.split('\t'); + const { css } = implementation.renderSync(sassOptions); - fs.writeFileSync(path.join(basePath, "spec", name, `${fileWithoutExt}.css`), css, "utf8"); - }); - }); + fs.writeFileSync( + path.join(basePath, 'spec', name, `${fileWithoutExt}.css`), + css, + 'utf8' + ); + }); + }); } module.exports = createSpec; diff --git a/test/tools/customFunctions.js b/test/tools/customFunctions.js index b1af218a..0bfd5ccb 100644 --- a/test/tools/customFunctions.js +++ b/test/tools/customFunctions.js @@ -1,18 +1,18 @@ -"use strict"; +'use strict'; module.exports = (implementation) => { - return { - "headings($from: 0, $to: 6)": (from, to) => { - const f = from.getValue(); - const t = to.getValue(); - const list = new implementation.types.List(t - f + 1); - let i; + return { + 'headings($from: 0, $to: 6)': (from, to) => { + const f = from.getValue(); + const t = to.getValue(); + const list = new implementation.types.List(t - f + 1); + let i; - for (i = f; i <= t; i++) { - list.setValue(i - f, new implementation.types.String(`h${ i}`)); - } + for (i = f; i <= t; i++) { + list.setValue(i - f, new implementation.types.String(`h${i}`)); + } - return list; - } - }; + return list; + }, + }; }; diff --git a/test/tools/customImporter.js b/test/tools/customImporter.js index 11c03e94..42e15c1d 100644 --- a/test/tools/customImporter.js +++ b/test/tools/customImporter.js @@ -1,18 +1,18 @@ -"use strict"; +'use strict'; -require("should"); +require('should'); function customImporter(path, prev) { - path.should.equal("import-with-custom-logic"); - prev.should.match(/(sass|scss)[/\\]custom-importer\.(scss|sass)/); + path.should.equal('import-with-custom-logic'); + prev.should.match(/(sass|scss)[/\\]custom-importer\.(scss|sass)/); - this.should.have.property("options"); // eslint-disable-line no-invalid-this + this.should.have.property('options'); // eslint-disable-line no-invalid-this - return customImporter.returnValue; + return customImporter.returnValue; } customImporter.returnValue = { - contents: ".custom-imported {}" + contents: '.custom-imported {}', }; module.exports = customImporter; diff --git a/test/tools/runCreateSpec.js b/test/tools/runCreateSpec.js index 437e3f04..1e70ae2a 100644 --- a/test/tools/runCreateSpec.js +++ b/test/tools/runCreateSpec.js @@ -1,7 +1,7 @@ -"use strict"; +'use strict'; -const createSpec = require("./createSpec.js"); +const createSpec = require('./createSpec.js'); -["scss", "sass"].forEach((ext) => { - createSpec(ext); +['scss', 'sass'].forEach((ext) => { + createSpec(ext); }); diff --git a/test/tools/testLoader.js b/test/tools/testLoader.js index 71729f65..97be5fc7 100644 --- a/test/tools/testLoader.js +++ b/test/tools/testLoader.js @@ -1,13 +1,13 @@ -"use strict"; +'use strict'; function testLoader(content, sourceMap) { - testLoader.content = content; - testLoader.sourceMap = sourceMap; + testLoader.content = content; + testLoader.sourceMap = sourceMap; - return ""; + return ''; } -testLoader.content = ""; +testLoader.content = ''; testLoader.sourceMap = null; testLoader.filename = __filename; diff --git a/test/watch/entry.js b/test/watch/entry.js index ad32ab60..e37b0eab 100644 --- a/test/watch/entry.js +++ b/test/watch/entry.js @@ -1,3 +1,3 @@ -"use strict"; +'use strict'; -require("../scss/imports.scss"); +require('../scss/imports.scss'); diff --git a/test/watch/webpack.config.js b/test/watch/webpack.config.js index 2aa42a6e..85d8e6f0 100644 --- a/test/watch/webpack.config.js +++ b/test/watch/webpack.config.js @@ -1,34 +1,38 @@ -"use strict"; +'use strict'; -const path = require("path"); +const path = require('path'); -const sassLoader = require.resolve("../../lib/loader"); +const sassLoader = require.resolve('../../lib/loader'); module.exports = { - entry: [ - path.resolve(__dirname, "../scss/imports.scss"), - path.resolve(__dirname, "../scss/import-include-paths.scss") + entry: [ + path.resolve(__dirname, '../scss/imports.scss'), + path.resolve(__dirname, '../scss/import-include-paths.scss'), + ], + output: { + path: path.resolve(__dirname, '../output'), + filename: 'bundle.watch.js', + }, + watch: true, + module: { + rules: [ + { + test: /\.scss$/, + use: [ + { + loader: 'style-loader', + }, + { + loader: 'css-loader', + }, + { + loader: sassLoader, + options: { + includePaths: [path.resolve(__dirname, '../scss/includePath')], + }, + }, + ], + }, ], - output: { - path: path.resolve(__dirname, "../output"), - filename: "bundle.watch.js" - }, - watch: true, - module: { - rules: [{ - test: /\.scss$/, - use: [{ - loader: "style-loader" - }, { - loader: "css-loader" - }, { - loader: sassLoader, - options: { - includePaths: [ - path.resolve(__dirname, "../scss/includePath") - ] - } - }] - }] - } + }, };