From 459f6dff15be8497e3a1c7964d581657671a070c Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Thu, 29 Sep 2022 17:15:29 -0400 Subject: [PATCH 1/5] [node-build-scripts] break: refactor into ES modules --- packages/node-build-scripts/.eslintrc.json | 3 +- ...ge-layout.js => assert-package-layout.mjs} | 5 +-- packages/node-build-scripts/constants.js | 8 ----- packages/node-build-scripts/constants.mjs | 6 ++++ .../{es-lint.js => es-lint.mjs} | 13 ++++---- ...ariables.js => generate-css-variables.mjs} | 32 ++++++++++--------- ...ctions.js => node-sass-json-functions.mjs} | 21 ++++++------ packages/node-build-scripts/package.json | 11 ++++--- packages/node-build-scripts/sass-compile.ts | 4 +++ .../{sass-lint.js => sass-lint.mjs} | 16 +++++++--- .../{utils.js => utils.mjs} | 15 ++++++--- 11 files changed, 75 insertions(+), 59 deletions(-) rename packages/node-build-scripts/{assert-package-layout.js => assert-package-layout.mjs} (93%) delete mode 100644 packages/node-build-scripts/constants.js create mode 100644 packages/node-build-scripts/constants.mjs rename packages/node-build-scripts/{es-lint.js => es-lint.mjs} (86%) rename packages/node-build-scripts/{generate-css-variables.js => generate-css-variables.mjs} (87%) rename packages/node-build-scripts/{node-sass-json-functions.js => node-sass-json-functions.mjs} (96%) rename packages/node-build-scripts/{sass-lint.js => sass-lint.mjs} (75%) rename packages/node-build-scripts/{utils.js => utils.mjs} (66%) diff --git a/packages/node-build-scripts/.eslintrc.json b/packages/node-build-scripts/.eslintrc.json index b96bfd5d63..c5af2f7c28 100644 --- a/packages/node-build-scripts/.eslintrc.json +++ b/packages/node-build-scripts/.eslintrc.json @@ -1,6 +1,7 @@ { "parserOptions": { - "ecmaVersion": 2020 + "ecmaVersion": 2022, + "sourceType": "module" }, "rules": { "no-console": "off" diff --git a/packages/node-build-scripts/assert-package-layout.js b/packages/node-build-scripts/assert-package-layout.mjs similarity index 93% rename from packages/node-build-scripts/assert-package-layout.js rename to packages/node-build-scripts/assert-package-layout.mjs index ade62be750..10a43243a0 100755 --- a/packages/node-build-scripts/assert-package-layout.js +++ b/packages/node-build-scripts/assert-package-layout.mjs @@ -5,8 +5,9 @@ */ // @ts-check -const fs = require("fs"); -const path = require("path"); + +import fs from "node:fs"; +import path from "node:path"; // asserts that all main fields in package.json reference existing files const PACKAGE_MAIN_FIELDS = ["main", "module", "style", "types", "typings", "unpkg"]; diff --git a/packages/node-build-scripts/constants.js b/packages/node-build-scripts/constants.js deleted file mode 100644 index f904cb1579..0000000000 --- a/packages/node-build-scripts/constants.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Copyright 2017 Palantir Technologies, Inc. All rights reserved. - */ - -module.exports = { - COPYRIGHT_HEADER: "/*\n * Copyright 2022 Palantir Technologies, Inc. All rights reserved.\n */\n", - USE_MATH_RULE: `@use "sass:math";\n`, -}; diff --git a/packages/node-build-scripts/constants.mjs b/packages/node-build-scripts/constants.mjs new file mode 100644 index 0000000000..fd5588b225 --- /dev/null +++ b/packages/node-build-scripts/constants.mjs @@ -0,0 +1,6 @@ +/** + * Copyright 2017 Palantir Technologies, Inc. All rights reserved. + */ + +export const COPYRIGHT_HEADER = "/*\n * Copyright 2022 Palantir Technologies, Inc. All rights reserved.\n */\n"; +export const USE_MATH_RULE = `@use "sass:math";\n`; diff --git a/packages/node-build-scripts/es-lint.js b/packages/node-build-scripts/es-lint.mjs similarity index 86% rename from packages/node-build-scripts/es-lint.js rename to packages/node-build-scripts/es-lint.mjs index a133931d1f..8ac86f4013 100755 --- a/packages/node-build-scripts/es-lint.js +++ b/packages/node-build-scripts/es-lint.mjs @@ -5,14 +5,13 @@ */ // @ts-check -"use strict"; -const { spawn } = require("cross-spawn"); -const fs = require("fs"); -const glob = require("glob"); -const path = require("path"); +import { spawn } from "cross-spawn"; +import glob from "glob"; +import fs from "node:fs"; +import path from "node:path"; -const { junitReportPath } = require("./utils"); +import { junitReportPath } from "./utils.mjs"; let format = "codeframe"; let out; @@ -50,5 +49,5 @@ eslint.stdout.pipe(outputStream); eslint.stderr.pipe(process.stderr); eslint.on("close", code => { - process.exitCode = code; + process.exitCode = code ?? undefined; }); diff --git a/packages/node-build-scripts/generate-css-variables.js b/packages/node-build-scripts/generate-css-variables.mjs similarity index 87% rename from packages/node-build-scripts/generate-css-variables.js rename to packages/node-build-scripts/generate-css-variables.mjs index 035e8739a0..503bffef9c 100755 --- a/packages/node-build-scripts/generate-css-variables.js +++ b/packages/node-build-scripts/generate-css-variables.mjs @@ -4,18 +4,19 @@ */ // @ts-check -const fs = require("fs"); -const getSassVars = require("get-sass-vars"); -const path = require("path"); -const prettier = require("prettier"); -const yargs = require("yargs/yargs"); -const { COPYRIGHT_HEADER, USE_MATH_RULE } = require("./constants"); +import getSassVars from "get-sass-vars"; +import fs from "node:fs"; +import path from "node:path"; +import prettier from "prettier"; +import yargs from "yargs/yargs"; + +import { COPYRIGHT_HEADER, USE_MATH_RULE } from "./constants.mjs"; const SRC_DIR = path.resolve(process.cwd(), "./src"); const DEST_DIR = path.resolve(process.cwd(), "./lib"); -main(); +await main(); async function main() { const args = yargs(process.argv.slice(2)) @@ -42,7 +43,7 @@ async function main() { * and gets compiled output from `get-sass-vars`. * * @param {string[]} inputSources - * @returns {Promise<{parsedVars: object, varsInBlocks: Set[], varsWithDefaultFlag: Set}>} output compiled variable values and grouped variable blocks + * @returns {Promise<{parsedVars: object, varsInBlocks: Set[], varsWithDefaultFlag: Set}>} output compiled variable values and grouped variable blocks */ async function getParsedVars(inputSources) { const stripCssComments = (await import("strip-css-comments")).default; @@ -62,8 +63,9 @@ async function getParsedVars(inputSources) { // @ts-ignore, issues with types in `get-sass-vars` const getSassVarsSync = getSassVars.sync; + const functions = (await import("./node-sass-json-functions.mjs")).default; const parsedVars = getSassVarsSync(cleanedInput, { - sassOptions: { functions: require("./node-sass-json-functions.js") }, + sassOptions: { functions }, }); // get variable blocks for separating variables in output @@ -71,15 +73,15 @@ async function getParsedVars(inputSources) { .split("\n\n") .map( block => - new Set([...block.matchAll(/(?\$[-_a-zA-z0-9]+)(?::)/g)].map(match => match.groups.varName)), + new Set([...block.matchAll(/(?\$[-_a-zA-z0-9]+)(?::)/g)].map(match => match.groups?.varName)), ); // `getSassVarsSync` strips `!default` flags from the output, so we need to determine which // variables had those flags set here and pass it on const varsWithDefaultFlag = new Set( [...cleanedInput.matchAll(/(?\$[-_a-zA-z0-9]+)(?::)(?[\s\S]+?);/gm)] - .map(match => [match.groups.varName, match.groups.varValue.trim()]) - .filter(([, varValue]) => varValue.endsWith("!default")) + .map(match => [match.groups?.varName, match.groups?.varValue.trim()]) + .filter(([, varValue]) => varValue?.endsWith("!default")) .map(([varName]) => varName), ); @@ -106,7 +108,7 @@ function convertParsedValueToOutput(value, outputType) { // Objects are map variables, formatted like: // https://lesscss.org/features/#maps-feature // https://sass-lang.com/documentation/values/maps - if (typeof value === "object") { + if (value !== null && typeof value === "object") { return outputType === "scss" ? `(${Object.entries(value).reduce( (str, [key, val]) => `${str}\n"${key}": ${convertParsedValueToOutput(val, outputType)},`, @@ -129,7 +131,7 @@ function convertParsedValueToOutput(value, outputType) { * Pulls together variables from the specified Sass source files, sanitizes them for consumption, * and writes to an output file. * - * @param {{parsedVars: object, varsInBlocks: Set[], varsWithDefaultFlag: Set}} parsedInput + * @param {{parsedVars: object, varsInBlocks: Set[], varsWithDefaultFlag: Set}} parsedInput * @param {string} outputFilename * @param {boolean} retainDefault whether to retain `!default` flags on variables * @returns {string} output Sass contents @@ -161,7 +163,7 @@ function generateScssVariables(parsedInput, outputFilename, retainDefault) { /** * Takes in variable values from compiled sass vars, converts them to Less and writes to an output file. * - * @param {{parsedVars: object, varsInBlocks: Set[]}} parsedInput + * @param {{parsedVars: object, varsInBlocks: Set[]}} parsedInput * @param {string} outputFilename * @returns {void} */ diff --git a/packages/node-build-scripts/node-sass-json-functions.js b/packages/node-build-scripts/node-sass-json-functions.mjs similarity index 96% rename from packages/node-build-scripts/node-sass-json-functions.js rename to packages/node-build-scripts/node-sass-json-functions.mjs index dbb48b64ce..6c58bc7ae0 100644 --- a/packages/node-build-scripts/node-sass-json-functions.js +++ b/packages/node-build-scripts/node-sass-json-functions.mjs @@ -23,15 +23,13 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -"use strict"; - -const isPlainObject = require("is-plain-obj"); -const parseColor = require("parse-color"); -const parseUnit = require("parse-css-dimension"); -const rgbHex = require("rgb-hex"); -const round = require("round-to"); -const sass = require("sass"); -const shortHexColor = require("shorten-css-hex"); +import isPlainObject from "is-plain-obj"; +import parseColor from "parse-color"; +import parseUnit from "parse-css-dimension"; +import rgbHex from "rgb-hex"; +import round from "round-to"; +import sass from "sass"; +import shortHexColor from "shorten-css-hex"; // eslint-disable-next-line no-underscore-dangle function _interopDefaultLegacy(e) { @@ -283,9 +281,8 @@ function decode(value) { } /** @type {{ 'json-encode($value, $quotes: true, $precision: 5)': typeof encode, 'json-decode($value)': typeof decode }} */ -const api = { +// eslint-disable-next-line import/no-default-export +export default { "json-encode($value, $quotes: true, $precision: 5)": encode, "json-decode($value)": decode, }; - -module.exports = api; diff --git a/packages/node-build-scripts/package.json b/packages/node-build-scripts/package.json index 2bb2706c31..52a72100ae 100644 --- a/packages/node-build-scripts/package.json +++ b/packages/node-build-scripts/package.json @@ -6,12 +6,12 @@ "test": "exit 0" }, "bin": { - "assert-package-layout": "./assert-package-layout.js", + "assert-package-layout": "./assert-package-layout.mjs", "css-dist": "./css-dist.sh", - "es-lint": "./es-lint.js", - "generate-css-variables": "./generate-css-variables.js", + "es-lint": "./es-lint.mjs", + "generate-css-variables": "./generate-css-variables.mjs", "sass-compile": "./sass-compile.sh", - "sass-lint": "./sass-lint.js" + "sass-lint": "./sass-lint.mjs" }, "dependencies": { "autoprefixer": "^10.4.4", @@ -42,6 +42,9 @@ "@types/fs-extra": "~9.0.13", "@types/yargs": "~17.0.13" }, + "engines": { + "node": ">=16" + }, "repository": { "type": "git", "url": "git@github.com:palantir/blueprint.git", diff --git a/packages/node-build-scripts/sass-compile.ts b/packages/node-build-scripts/sass-compile.ts index f4c5863666..4aeeaca839 100644 --- a/packages/node-build-scripts/sass-compile.ts +++ b/packages/node-build-scripts/sass-compile.ts @@ -1,3 +1,7 @@ +/** + * Copyright 2021 Palantir Technologies, Inc. All rights reserved. + */ + import { watch } from "chokidar"; import fs from "fs-extra"; import path from "path"; diff --git a/packages/node-build-scripts/sass-lint.js b/packages/node-build-scripts/sass-lint.mjs similarity index 75% rename from packages/node-build-scripts/sass-lint.js rename to packages/node-build-scripts/sass-lint.mjs index bf803220e6..06ae2f395d 100755 --- a/packages/node-build-scripts/sass-lint.js +++ b/packages/node-build-scripts/sass-lint.mjs @@ -5,17 +5,22 @@ */ // @ts-check -const fs = require("fs"); -const path = require("path"); -const stylelint = require("stylelint"); -const { junitReportPath } = require("./utils"); +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import stylelint from "stylelint"; + +import { junitReportPath } from "./utils.mjs"; const emitReport = process.env.JUNIT_REPORT_PATH != null; +/** Path to Stylelint config file */ +const configFile = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..", ".stylelintrc"); + stylelint .lint({ - configFile: path.resolve(__dirname, "../..", ".stylelintrc"), + configFile, files: "src/**/*.scss", formatter: emitReport ? require("stylelint-junit-formatter") : "string", customSyntax: "postcss-scss", @@ -26,6 +31,7 @@ stylelint // emit JUnit XML report to ///stylelint.xml when this env variable is set const reportPath = junitReportPath("stylelint"); console.info(`Stylelint report will appear in ${reportPath}`); + // @ts-ignore fs.writeFileSync(reportPath, resultObject.output); } else { console.info(resultObject.output); diff --git a/packages/node-build-scripts/utils.js b/packages/node-build-scripts/utils.mjs similarity index 66% rename from packages/node-build-scripts/utils.js rename to packages/node-build-scripts/utils.mjs index 455d05e4cc..f9115bc5bf 100644 --- a/packages/node-build-scripts/utils.js +++ b/packages/node-build-scripts/utils.mjs @@ -15,14 +15,19 @@ */ // @ts-check -const path = require("path"); + +import path from "node:path"; +import { fileURLToPath } from "node:url"; /** * @param {string} dirName name of directory containing XML file. * @param {string} fileName name of XML file (defaults to current directory name). */ -function junitReportPath(dirName, fileName = path.basename(process.cwd())) { - return path.join(__dirname, "../..", process.env.JUNIT_REPORT_PATH, dirName, `${fileName}.xml`); -} +export function junitReportPath(dirName, fileName = path.basename(process.cwd())) { + if (process.env.JUNIT_REPORT_PATH === undefined) { + return undefined; + } -module.exports = { junitReportPath }; + const dirname = path.dirname(fileURLToPath(import.meta.url)); + return path.join(dirname, "../..", process.env.JUNIT_REPORT_PATH, dirName, `${fileName}.xml`); +} From ae25a28c7f8a8485cd925d606aa9ce46ec2937c3 Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Thu, 29 Sep 2022 18:03:23 -0400 Subject: [PATCH 2/5] require() -> import(); bump CI cache key --- .circleci/config.yml | 8 ++++---- packages/node-build-scripts/assert-package-layout.mjs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d287e06d46..287275492a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -14,23 +14,23 @@ aliases: - &restore-node-modules-cache name: Restore node_modules cache - key: v5-yarn-deps-{{ checksum "yarn.lock" }} + key: v6-yarn-deps-{{ checksum "yarn.lock" }} - &restore-yarn-cache name: Restore yarnpkg cache - key: v5-yarn-cache + key: v6-yarn-cache - &save-node-modules-cache name: Save node_modules cache paths: - node_modules - key: v5-yarn-deps-{{ checksum "yarn.lock" }} + key: v6-yarn-deps-{{ checksum "yarn.lock" }} - &save-yarn-cache name: Save yarnpkg cache paths: - ~/.cache/yarn - key: v5-yarn-cache + key: v6-yarn-cache references: reports_path: &reports_path diff --git a/packages/node-build-scripts/assert-package-layout.mjs b/packages/node-build-scripts/assert-package-layout.mjs index 10a43243a0..d133a9ed5c 100755 --- a/packages/node-build-scripts/assert-package-layout.mjs +++ b/packages/node-build-scripts/assert-package-layout.mjs @@ -11,7 +11,7 @@ import path from "node:path"; // asserts that all main fields in package.json reference existing files const PACKAGE_MAIN_FIELDS = ["main", "module", "style", "types", "typings", "unpkg"]; -const manifest = require(path.resolve(process.cwd(), "./package.json")); +const manifest = await import(path.resolve(process.cwd(), "package.json")); for (const field of PACKAGE_MAIN_FIELDS.filter(f => manifest[f] !== undefined)) { if (!fs.existsSync(path.resolve(process.cwd(), manifest[field]))) { From d43fd20ac90fc99106365f3c6aa2bb4324d42f2e Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Fri, 30 Sep 2022 09:29:13 -0400 Subject: [PATCH 3/5] fix .json require --- .../node-build-scripts/assert-package-layout.mjs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/node-build-scripts/assert-package-layout.mjs b/packages/node-build-scripts/assert-package-layout.mjs index d133a9ed5c..7db421fadb 100755 --- a/packages/node-build-scripts/assert-package-layout.mjs +++ b/packages/node-build-scripts/assert-package-layout.mjs @@ -6,21 +6,25 @@ // @ts-check -import fs from "node:fs"; -import path from "node:path"; +import { existsSync, readFileSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { cwd, exit } from "node:process"; +import { pathToFileURL } from "node:url"; // asserts that all main fields in package.json reference existing files const PACKAGE_MAIN_FIELDS = ["main", "module", "style", "types", "typings", "unpkg"]; -const manifest = await import(path.resolve(process.cwd(), "package.json")); + +// TODO(adahiya): replace this with `await import("./package.json", { assert: { type: "json" } })` in Node 17.5+ +const manifest = JSON.parse(readFileSync(pathToFileURL(join(cwd(), "package.json")), { encoding: "utf8" })); for (const field of PACKAGE_MAIN_FIELDS.filter(f => manifest[f] !== undefined)) { - if (!fs.existsSync(path.resolve(process.cwd(), manifest[field]))) { + if (!existsSync(resolve(cwd(), manifest[field]))) { console.error( `[node-build-scripts] Failed to validate package layout: expected '${manifest[field]}' to exist.`, ); - process.exit(1); + exit(1); } } console.info("[node-build-scripts] Successfully validated package layout."); -process.exit(0); +exit(0); From 9a8585bf040987c3689af5fa0fd9c15bd0db4401 Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Fri, 30 Sep 2022 09:32:09 -0400 Subject: [PATCH 4/5] import stylelint-junit-formatter correctly --- packages/node-build-scripts/sass-lint.mjs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/node-build-scripts/sass-lint.mjs b/packages/node-build-scripts/sass-lint.mjs index 06ae2f395d..48a4c1b069 100755 --- a/packages/node-build-scripts/sass-lint.mjs +++ b/packages/node-build-scripts/sass-lint.mjs @@ -6,23 +6,24 @@ // @ts-check -import fs from "node:fs"; -import path from "node:path"; +import { writeFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import stylelint from "stylelint"; +import stylelintJUnitFormater from "stylelint-junit-formatter"; import { junitReportPath } from "./utils.mjs"; const emitReport = process.env.JUNIT_REPORT_PATH != null; /** Path to Stylelint config file */ -const configFile = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..", ".stylelintrc"); +const configFile = resolve(dirname(fileURLToPath(import.meta.url)), "../..", ".stylelintrc"); stylelint .lint({ configFile, files: "src/**/*.scss", - formatter: emitReport ? require("stylelint-junit-formatter") : "string", + formatter: emitReport ? stylelintJUnitFormater : "string", customSyntax: "postcss-scss", fix: process.argv.indexOf("--fix") > 0, }) @@ -32,7 +33,7 @@ stylelint const reportPath = junitReportPath("stylelint"); console.info(`Stylelint report will appear in ${reportPath}`); // @ts-ignore - fs.writeFileSync(reportPath, resultObject.output); + writeFileSync(reportPath, resultObject.output); } else { console.info(resultObject.output); } From 818c89c844134ce118151568d9c46542bc6bdca8 Mon Sep 17 00:00:00 2001 From: Adi Dahiya Date: Fri, 30 Sep 2022 10:23:04 -0400 Subject: [PATCH 5/5] refactor to use named imports and 'node:process' module --- packages/node-build-scripts/es-lint.mjs | 31 ++++++++++--------- .../generate-css-variables.mjs | 31 +++++++++---------- packages/node-build-scripts/sass-compile.ts | 31 ++++++++++--------- packages/node-build-scripts/sass-lint.mjs | 24 +++++++------- packages/node-build-scripts/utils.mjs | 23 +++++++++++--- 5 files changed, 76 insertions(+), 64 deletions(-) diff --git a/packages/node-build-scripts/es-lint.mjs b/packages/node-build-scripts/es-lint.mjs index 8ac86f4013..17937396f6 100755 --- a/packages/node-build-scripts/es-lint.mjs +++ b/packages/node-build-scripts/es-lint.mjs @@ -8,46 +8,47 @@ import { spawn } from "cross-spawn"; import glob from "glob"; -import fs from "node:fs"; -import path from "node:path"; +import { createWriteStream } from "node:fs"; +import { basename, resolve } from "node:path"; +import { argv, cwd, env, exit, stderr, stdout } from "node:process"; import { junitReportPath } from "./utils.mjs"; let format = "codeframe"; -let out; -let outputStream = process.stdout; -if (process.env.JUNIT_REPORT_PATH != null) { +let outputPath; +let outputStream = stdout; +if (env.JUNIT_REPORT_PATH != null) { format = "junit"; - out = junitReportPath("eslint"); - console.info(`ESLint report will appear in ${out}`); + outputPath = junitReportPath("eslint"); + console.info(`[node-build-scripts] ESLint report will appear in ${outputPath}`); // @ts-ignore - outputStream = fs.createWriteStream(out, { flags: "w+" }); + outputStream = createWriteStream(outputPath, { flags: "w+" }); } // additional args provided to es-lint script -const additionalArgs = process.argv.filter(a => { +const additionalArgs = argv.filter(a => { // exclude engine and script name - return ["node", "node.exe", "es-lint", "es-lint.js"].every(s => path.basename(a) !== s); + return ["node", "node.exe", "es-lint", "es-lint.js"].every(s => basename(a) !== s); }); const commandLineOptions = ["--color", "-f", format, ...additionalArgs]; // ESLint will fail if provided with no files, so we expand the glob before running it const fileGlob = "{src,test}/**/*.{ts,tsx}"; -const absoluteFileGlob = path.resolve(process.cwd(), fileGlob); +const absoluteFileGlob = resolve(cwd(), fileGlob); const anyFilesToLint = glob.sync(absoluteFileGlob); if (anyFilesToLint.length === 0) { console.log(`[node-build-scripts] Not running ESLint because no files match the glob "${fileGlob}"`); - process.exit(); + exit(); } -process.env.LINT_SCRIPT = "true"; +env.LINT_SCRIPT = "true"; const eslint = spawn("eslint", [...commandLineOptions, absoluteFileGlob]); eslint.stdout.pipe(outputStream); -eslint.stderr.pipe(process.stderr); +eslint.stderr.pipe(stderr); eslint.on("close", code => { - process.exitCode = code ?? undefined; + exit(code ?? undefined); }); diff --git a/packages/node-build-scripts/generate-css-variables.mjs b/packages/node-build-scripts/generate-css-variables.mjs index 503bffef9c..75786683f7 100755 --- a/packages/node-build-scripts/generate-css-variables.mjs +++ b/packages/node-build-scripts/generate-css-variables.mjs @@ -6,20 +6,21 @@ // @ts-check import getSassVars from "get-sass-vars"; -import fs from "node:fs"; -import path from "node:path"; +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { argv, cwd } from "node:process"; import prettier from "prettier"; import yargs from "yargs/yargs"; import { COPYRIGHT_HEADER, USE_MATH_RULE } from "./constants.mjs"; -const SRC_DIR = path.resolve(process.cwd(), "./src"); -const DEST_DIR = path.resolve(process.cwd(), "./lib"); +const SRC_DIR = resolve(cwd(), "./src"); +const DEST_DIR = resolve(cwd(), "./lib"); await main(); async function main() { - const args = yargs(process.argv.slice(2)) + const args = yargs(argv.slice(2)) .option("outputFileName", { alias: "o", default: "variables", @@ -49,7 +50,7 @@ async function getParsedVars(inputSources) { const stripCssComments = (await import("strip-css-comments")).default; // concatenate sources let cleanedInput = inputSources.reduce((str, currentFilename) => { - return str + fs.readFileSync(`${SRC_DIR}/${currentFilename}`).toString(); + return str + readFileSync(`${SRC_DIR}/${currentFilename}`).toString(); }, ""); // strip comments, clean up for consumption cleanedInput = stripCssComments(cleanedInput); @@ -60,11 +61,9 @@ async function getParsedVars(inputSources) { .replace(/\n{3,}/g, "\n\n"); cleanedInput = [USE_MATH_RULE, cleanedInput].join("\n"); - // @ts-ignore, issues with types in `get-sass-vars` - const getSassVarsSync = getSassVars.sync; - const functions = (await import("./node-sass-json-functions.mjs")).default; - const parsedVars = getSassVarsSync(cleanedInput, { + const parsedVars = await getSassVars(cleanedInput, { + // @ts-ignore - `get-sass-vars` types do not capture the type constraints possible with the library sassOptions: { functions }, }); @@ -153,10 +152,10 @@ function generateScssVariables(parsedInput, outputFilename, retainDefault) { const formattedVariablesScss = prettier.format(variablesScss, { parser: "less" }); - if (!fs.existsSync(`${DEST_DIR}/scss`)) { - fs.mkdirSync(`${DEST_DIR}/scss`, { recursive: true }); + if (!existsSync(`${DEST_DIR}/scss`)) { + mkdirSync(`${DEST_DIR}/scss`, { recursive: true }); } - fs.writeFileSync(`${DEST_DIR}/scss/${outputFilename}.scss`, formattedVariablesScss); + writeFileSync(`${DEST_DIR}/scss/${outputFilename}.scss`, formattedVariablesScss); return formattedVariablesScss; } @@ -199,8 +198,8 @@ function generateLessVariables(parsedInput, outputFilename) { const formattedVariablesLess = prettier.format(variablesLess, { parser: "less" }); - if (!fs.existsSync(`${DEST_DIR}/less`)) { - fs.mkdirSync(`${DEST_DIR}/less`, { recursive: true }); + if (!existsSync(`${DEST_DIR}/less`)) { + mkdirSync(`${DEST_DIR}/less`, { recursive: true }); } - fs.writeFileSync(`${DEST_DIR}/less/${outputFilename}.less`, formattedVariablesLess); + writeFileSync(`${DEST_DIR}/less/${outputFilename}.less`, formattedVariablesLess); } diff --git a/packages/node-build-scripts/sass-compile.ts b/packages/node-build-scripts/sass-compile.ts index 4aeeaca839..4899cb21e3 100644 --- a/packages/node-build-scripts/sass-compile.ts +++ b/packages/node-build-scripts/sass-compile.ts @@ -3,14 +3,15 @@ */ import { watch } from "chokidar"; -import fs from "fs-extra"; -import path from "path"; -import * as sass from "sass"; +import { outputFileSync, readdirSync } from "fs-extra"; +import { basename, dirname, extname, join, parse as parsePath, relative, resolve } from "node:path"; +import { argv } from "node:process"; +import sass from "sass"; import nodeSassPackageImporter from "node-sass-package-importer"; import { RawSourceMap } from "source-map"; import yargs from "yargs"; -const args = yargs(process.argv.slice(2)) +const args = yargs(argv.slice(2)) .positional("input", { type: "string", description: "Input folder containing scss to compile" }) .option("functions", { type: "string", @@ -24,19 +25,19 @@ const args = yargs(process.argv.slice(2)) }) .parseSync(); -const functions = args.functions != null ? require(path.resolve(args.functions)) : undefined; +const functions = args.functions != null ? require(resolve(args.functions)) : undefined; if (args.watch) { compileAllFiles(); - const folderToWatch = path.resolve(args._[0] as string); + const folderToWatch = resolve(args._[0] as string); console.info(`[sass-compile] Watching ${folderToWatch} for changes...`); const watcher = watch([`${folderToWatch}/*.scss`, `${folderToWatch}/**/*.scss`], { persistent: true }); watcher.on("change", fileName => { console.info(`[sass-compile] Detected change in ${fileName}, re-compiling.`); - if (path.basename(fileName).startsWith("_")) { + if (basename(fileName).startsWith("_")) { compileAllFiles(); } else { compileFile(fileName); @@ -47,10 +48,10 @@ if (args.watch) { } function compileAllFiles() { - const files = fs.readdirSync(args._[0] as string); + const files = readdirSync(args._[0] as string); const inputFiles = files - .filter(file => path.extname(file) === ".scss" && !path.basename(file).startsWith("_")) - .map(fileName => path.join(args._[0] as string, fileName)); + .filter(file => extname(file) === ".scss" && !basename(file).startsWith("_")) + .map(fileName => join(args._[0] as string, fileName)); for (const inputFile of inputFiles) { compileFile(inputFile); @@ -63,7 +64,7 @@ function compileFile(inputFile: string) { throw new Error(`[sass-compile] Output folder must be specified with --output CLI argument.`); } - const outFile = path.join(args.output, `${path.parse(inputFile).name}.css`); + const outFile = join(args.output, `${parsePath(inputFile).name}.css`); const outputMapFile = `${outFile}.map`; // use deprecated `renderSync` because it supports legacy importers and functions const result = sass.renderSync({ @@ -75,9 +76,9 @@ function compileFile(inputFile: string) { functions, charset: true, }); - fs.outputFileSync(outFile, result.css, { flag: "w" }); + outputFileSync(outFile, result.css, { flag: "w" }); if (result.map != null) { - fs.outputFileSync(outputMapFile, fixSourcePathsInSourceMap({ outputMapFile, sourceMapBuffer: result.map }), { + outputFileSync(outputMapFile, fixSourcePathsInSourceMap({ outputMapFile, sourceMapBuffer: result.map }), { flag: "w", }); } @@ -92,9 +93,9 @@ function fixSourcePathsInSourceMap({ }): string { const parsedMap = JSON.parse(sourceMapBuffer.toString()) as RawSourceMap; parsedMap.sources = parsedMap.sources.map(source => { - const outputDirectory = path.dirname(outputMapFile); + const outputDirectory = dirname(outputMapFile); const pathToSourceWithoutProtocol = source.replace("file://", ""); - return path.relative(outputDirectory, pathToSourceWithoutProtocol); + return relative(outputDirectory, pathToSourceWithoutProtocol); }); return JSON.stringify(parsedMap); } diff --git a/packages/node-build-scripts/sass-lint.mjs b/packages/node-build-scripts/sass-lint.mjs index 48a4c1b069..6c82120c48 100755 --- a/packages/node-build-scripts/sass-lint.mjs +++ b/packages/node-build-scripts/sass-lint.mjs @@ -7,42 +7,40 @@ // @ts-check import { writeFileSync } from "node:fs"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; +import { join } from "node:path"; +import { argv, exit } from "node:process"; import stylelint from "stylelint"; import stylelintJUnitFormater from "stylelint-junit-formatter"; -import { junitReportPath } from "./utils.mjs"; +import { getRootDir, junitReportPath } from "./utils.mjs"; -const emitReport = process.env.JUNIT_REPORT_PATH != null; +// emit JUnit XML report to ///stylelint.xml when env.JUNIT_REPORT_PATH is set +const reportPath = junitReportPath("stylelint"); /** Path to Stylelint config file */ -const configFile = resolve(dirname(fileURLToPath(import.meta.url)), "../..", ".stylelintrc"); +const configFile = join(getRootDir(), ".stylelintrc"); stylelint .lint({ configFile, files: "src/**/*.scss", - formatter: emitReport ? stylelintJUnitFormater : "string", + formatter: reportPath !== undefined ? stylelintJUnitFormater : "string", customSyntax: "postcss-scss", - fix: process.argv.indexOf("--fix") > 0, + fix: argv.indexOf("--fix") > 0, }) .then(resultObject => { - if (emitReport) { - // emit JUnit XML report to ///stylelint.xml when this env variable is set - const reportPath = junitReportPath("stylelint"); + if (reportPath !== undefined) { console.info(`Stylelint report will appear in ${reportPath}`); - // @ts-ignore writeFileSync(reportPath, resultObject.output); } else { console.info(resultObject.output); } if (resultObject.errored) { - process.exitCode = 2; + exit(2); } }) .catch(error => { console.error("[node-build-scripts] sass-lint failed with error:"); console.error(error); - process.exitCode = 2; + exit(2); }); diff --git a/packages/node-build-scripts/utils.mjs b/packages/node-build-scripts/utils.mjs index f9115bc5bf..ee1f039b45 100644 --- a/packages/node-build-scripts/utils.mjs +++ b/packages/node-build-scripts/utils.mjs @@ -16,18 +16,31 @@ // @ts-check -import path from "node:path"; +import { basename, dirname, join, resolve } from "node:path"; +import { cwd, env } from "node:process"; import { fileURLToPath } from "node:url"; /** * @param {string} dirName name of directory containing XML file. * @param {string} fileName name of XML file (defaults to current directory name). */ -export function junitReportPath(dirName, fileName = path.basename(process.cwd())) { - if (process.env.JUNIT_REPORT_PATH === undefined) { +export function junitReportPath(dirName, fileName = basename(cwd())) { + if (env.JUNIT_REPORT_PATH === undefined) { return undefined; } - const dirname = path.dirname(fileURLToPath(import.meta.url)); - return path.join(dirname, "../..", process.env.JUNIT_REPORT_PATH, dirName, `${fileName}.xml`); + return join(getRootDir(), env.JUNIT_REPORT_PATH, dirName, `${fileName}.xml`); +} + +/** + * WARNING: this function only works inside the palantir/blueprint monorepo. It is currently broken for + * consumers who use @blueprintjs/node-build-scripts as an NPM dependency. + * + * @see https://github.com/palantir/blueprint/issues/5295 + * @see https://github.com/palantir/blueprint/issues/4942 + * + * @returns the root directory of this Blueprint monorepo + */ +export function getRootDir() { + return resolve(dirname(fileURLToPath(import.meta.url)), "..", ".."); }