From 1d7a889803cbebed77cd70922ebdf1a837d032a7 Mon Sep 17 00:00:00 2001 From: m-allanson Date: Thu, 5 Nov 2020 12:52:38 +0000 Subject: [PATCH] Clearer naming --- lib/core-entrypoint.js | 139 +++++++++++++++++++++++++++++++++++++++++ scripts/bundle-core.js | 102 ++++++++++++++++++++++++++++++ 2 files changed, 241 insertions(+) create mode 100644 lib/core-entrypoint.js create mode 100644 scripts/bundle-core.js diff --git a/lib/core-entrypoint.js b/lib/core-entrypoint.js new file mode 100644 index 0000000000..309571c449 --- /dev/null +++ b/lib/core-entrypoint.js @@ -0,0 +1,139 @@ +'use strict'; + +const LazyResult = require('postcss/lib/lazy-result'); +const postcss = require('postcss'); +const postcssSafeParser = require('postcss-safe-parser'); + +const createPartialStylelintResult = require('./createPartialStylelintResult'); +const jsonFormatter = require('./formatters/jsonFormatter'); +const lintPostcssResult = require('./lintPostcssResult'); +const normalizeAllRuleSettings = require('./normalizeAllRuleSettings'); +const prepareReturnValue = require('./prepareReturnValue'); + +const postcssProcessor = postcss(); + +/** @typedef {import('stylelint').StylelintStandaloneOptions} StylelintStandaloneOptions */ +/** @typedef {import('stylelint').StylelintStandaloneReturnValue} StylelintStandaloneReturnValue */ +/** @typedef {import('stylelint').StylelintResult} StylelintResult */ +/** @typedef {import('stylelint').StylelintInternalApi} StylelintInternalApi */ +/** @typedef {import('postcss').Syntax} Syntax */ + +/** TODO: create new type that's a subset of StylelintStandaloneOptions */ +/** + * @param {StylelintStandaloneOptions} options + * @returns {Promise} + */ +async function lint(options) { + if (!options.code) { + throw new Error( + `"options.code" is missing. You must provide a string value in "options.code".`, + ); + } + + if (!options.config) { + throw new Error( + `"options.config" is missing. You must provide a config object in "options.config".`, + ); + } + + if (options.formatter) { + throw new Error( + `"options.formatter" is not supported for stylelint's browser bundle. Remove the formatter option or try the standalone version of stylelint. TODO: link here?`, + ); + } + + if (options.syntax) { + throw new Error( + `"options.syntax" is not supported for stylelint's browser bundle. You must load a syntax and pass it in to "options.customSyntax". Refer to the stylelint browser bundle docs for more info. TODO: url here`, + ); + } + + if (options.config.plugins) { + throw new Error( + `"options.config.plugins" is not supported for stylelint's browser bundle. Remove the plugins from your config or try the standalone version of stylelint. TODO: url here`, + ); + } + + // TODO: more input validation? check for other options that aren't supported here + + const syntax = syntaxFromOptions(options); + + const postcssOptions = { + syntax, + from: undefined, + }; + + let cssResult; + let stylelintResult /** @type StylelintResult */; + + try { + const lazyResult = await new LazyResult(postcssProcessor, options.code, postcssOptions); + + cssResult = Object.assign(lazyResult, { + stylelint: { + ruleSeverities: {}, + customMessages: {}, + disabledRanges: {}, + }, + }); + + const normalizedConfig = normalizeAllRuleSettings(options.config); + + await lintPostcssResult(options, cssResult, normalizedConfig); + + stylelintResult = createPartialStylelintResult(cssResult); + } catch (error) { + // This is equivalent to the `handleError` function in standalone.js + if (error.name === 'CssSyntaxError') { + stylelintResult = createPartialStylelintResult(undefined, error); + } else { + throw error; + } + } + + // TODO: this is mostly the same in standalone.js. Extract out? + const postcssResult = stylelintResult._postcssResult; + const returnValue = prepareReturnValue([stylelintResult], options, jsonFormatter); + + if (options.fix && postcssResult && !postcssResult.stylelint.ignored) { + if (!postcssResult.stylelint.disableWritingFix) { + // If we're fixing, the output should be the fixed code + // @ts-ignore note: ts complaining that .root might not exist + returnValue.output = postcssResult.root.toString(syntax); + } else { + // If the writing of the fix is disabled, the input code is returned as-is + returnValue.output = options.code; + } + } + + return returnValue; +} + +/** + * @param {StylelintStandaloneOptions} options + * @returns {Syntax} + */ +function syntaxFromOptions(options) { + const { customSyntax, fix } = options; + + if (!customSyntax) + return { + parse: fix ? postcssSafeParser : postcss.parse, + stringify: postcss.stringify, + }; + + if ( + customSyntax && + typeof customSyntax === 'object' && + typeof customSyntax.parse == 'function' && + typeof customSyntax.stringify == 'function' + ) { + return customSyntax; + } + + throw new Error( + `Provided customSyntax is invalid. You must provide an object containing 'parse' and 'stringify' methods. TODO docs link?`, + ); +} + +module.exports = { lint }; diff --git a/scripts/bundle-core.js b/scripts/bundle-core.js new file mode 100644 index 0000000000..6cd04cd883 --- /dev/null +++ b/scripts/bundle-core.js @@ -0,0 +1,102 @@ +'use strict'; + +/* eslint-disable node/no-extraneous-require */ + +const bundleReport = require('@parcel/reporter-cli/lib/bundleReport').default; +const defaultConfigContents = require('@parcel/config-default'); +const defaultConfigFilePath = require.resolve('@parcel/config-default'); +const Parcel = require('@parcel/core').default; +const { createWorkerFarm } = require('@parcel/core'); + +/* eslint-enable node/no-extraneous-require */ + +// TODO: maybe it's worth verifying that all syntaxes are bundled? +// Though this is unlikely to change very often. + +const config = { + browser: { + target: { distDir: 'dist' }, + entry: 'lib/core-entrypoint.js', + }, + 'syntax-css-in-js': { + target: { distDir: 'dist' }, + entry: 'lib/syntaxes/syntax-css-in-js.js', + }, + 'syntax-html': { + target: { distDir: 'dist' }, + entry: 'lib/syntaxes/syntax-html.js', + }, + 'syntax-less': { + target: { distDir: 'dist' }, + entry: 'lib/syntaxes/syntax-less.js', + }, + 'syntax-markdown': { + target: { distDir: 'dist' }, + entry: 'lib/syntaxes/syntax-markdown.js', + }, + 'syntax-sass': { + target: { distDir: 'dist' }, + entry: 'lib/syntaxes/syntax-sass.js', + }, + 'syntax-scss': { + target: { distDir: 'dist' }, + entry: 'lib/syntaxes/syntax-scss.js', + }, + 'syntax-sugarss': { + target: { distDir: 'dist' }, + entry: 'lib/syntaxes/syntax-sugarss.js', + }, +}; + +// Workers have a startup cost. We can keep the workers around between runs, +// then shut them down when all bundles have been created. +let workerFarm = createWorkerFarm(); + +let tasks = []; + +// eslint-disable-next-line guard-for-in -- meh +for (const module in config) { + let task = async () => { + let bundler = new Parcel({ + entries: config[module].entry, + targets: { [module]: config[module].target }, + defaultConfig: { + ...defaultConfigContents, + filePath: defaultConfigFilePath, + }, + defaultEngines: { + browsers: ['> 0.25%'], + }, + includeNodeModules: true, + isLibrary: false, + logLevel: 'warn', + minify: true, + mode: 'production', + outputFormat: 'esmodule', + patchConsole: false, + workerFarm, + }); + + const bundleGraph = await bundler.run(); + const options = bundler._getResolvedParcelOptions(); // hackety hack. Not a public API + + // log stats to console. This is similar to setting `logLevel: verbose`, but skips the progress spinner to avoid unnecessary output + await bundleReport(bundleGraph, options.outputFS, options.projectRoot, options.detailedReport); + + return bundleGraph; + }; + + tasks.push(task); +} + +(async () => { + try { + for (const task of tasks) { + await task(); + } + } catch (error) { + console.error(error); // eslint-disable-line no-console + } finally { + await workerFarm.end(); // finished bundling, so we can shut the farm. + } +})();