Merge branch 'main' into next
# Conflicts: # package.json # yarn.lock
This commit is contained in:
commit
9697144462
331 files changed
+14280
-11478
No files matched your search
+44
-25
@@ -3,10 +3,10 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import url from "node:url";
|
||||
import createEsmUtils from "esm-utils";
|
||||
import { execa } from "execa";
|
||||
import esbuild from "esbuild";
|
||||
import fastGlob from "fast-glob";
|
||||
import { format } from "../src/index.js";
|
||||
import spawn from "nano-spawn";
|
||||
import serialize from "serialize-javascript";
|
||||
import {
|
||||
copyFile,
|
||||
DIST_DIR,
|
||||
@@ -16,19 +16,22 @@ import {
|
||||
writeJson,
|
||||
} from "./utils/index.js";
|
||||
|
||||
const { require } = createEsmUtils(import.meta);
|
||||
const runYarn = (command, args, options) =>
|
||||
execa("yarn", [command, ...args], {
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
...options,
|
||||
});
|
||||
spawn("yarn", [command, ...args], { stdio: "inherit", ...options });
|
||||
const IS_PULL_REQUEST = process.env.PULL_REQUEST === "true";
|
||||
const PRETTIER_DIR = IS_PULL_REQUEST
|
||||
? DIST_DIR
|
||||
? path.join(DIST_DIR, "prettier")
|
||||
: url.fileURLToPath(new URL("../node_modules/prettier", import.meta.url));
|
||||
const PLAYGROUND_PRETTIER_DIR = path.join(WEBSITE_DIR, "static/lib");
|
||||
|
||||
async function writeScript(file, code) {
|
||||
const { code: minified } = await esbuild.transform(code, {
|
||||
loader: "js",
|
||||
minify: true,
|
||||
});
|
||||
await writeFile(path.join(PLAYGROUND_PRETTIER_DIR, file), minified.trim());
|
||||
}
|
||||
|
||||
async function buildPrettier() {
|
||||
// --- Build prettier for PR ---
|
||||
const packageJsonFile = path.join(PROJECT_ROOT, "package.json");
|
||||
@@ -50,7 +53,7 @@ async function buildPrettier() {
|
||||
}
|
||||
|
||||
async function buildPlaygroundFiles() {
|
||||
const patterns = ["standalone.js", "plugins/*.js"];
|
||||
const patterns = ["standalone.mjs", "plugins/*.mjs"];
|
||||
|
||||
const files = await fastGlob(patterns, {
|
||||
cwd: PRETTIER_DIR,
|
||||
@@ -64,31 +67,47 @@ async function buildPlaygroundFiles() {
|
||||
const dist = path.join(PLAYGROUND_PRETTIER_DIR, fileName);
|
||||
await copyFile(file, dist);
|
||||
|
||||
if (fileName === "standalone.js") {
|
||||
if (fileName === "standalone.mjs") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const pluginModule = require(dist);
|
||||
const plugin = pluginModule.default ?? pluginModule;
|
||||
const { parsers = {}, printers = {} } = plugin;
|
||||
packageManifest.builtinPlugins.push({
|
||||
const pluginModule = await import(url.pathToFileURL(dist));
|
||||
|
||||
const plugin = {
|
||||
name: path.basename(fileName, ".js"),
|
||||
file: fileName,
|
||||
parsers: Object.keys(parsers),
|
||||
printers: Object.keys(printers),
|
||||
});
|
||||
};
|
||||
|
||||
for (const property of ["languages", "options", "defaultOptions"]) {
|
||||
const value = pluginModule[property];
|
||||
if (value !== undefined) {
|
||||
plugin[property] = value;
|
||||
}
|
||||
}
|
||||
|
||||
for (const property of ["parsers", "printers"]) {
|
||||
const value = pluginModule[property];
|
||||
if (value !== undefined) {
|
||||
plugin[property] = Object.keys(value);
|
||||
}
|
||||
}
|
||||
|
||||
packageManifest.builtinPlugins.push(plugin);
|
||||
}
|
||||
|
||||
await writeFile(
|
||||
path.join(PLAYGROUND_PRETTIER_DIR, "package-manifest.js"),
|
||||
await format(
|
||||
const serialized = serialize(packageManifest, { space: 2 });
|
||||
|
||||
await Promise.all([
|
||||
writeScript("package-manifest.mjs", `export default ${serialized};`),
|
||||
writeScript(
|
||||
"package-manifest.js",
|
||||
/* Indent */ `
|
||||
"use strict";
|
||||
|
||||
const prettierPackageManifest = ${JSON.stringify(packageManifest)};
|
||||
self.prettierPackageManifest = ${serialized};
|
||||
`,
|
||||
{ parser: "meriyah" },
|
||||
),
|
||||
);
|
||||
]);
|
||||
}
|
||||
|
||||
if (IS_PULL_REQUEST) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { outdent } from "outdent";
|
||||
import rollupPluginLicense from "rollup-plugin-license";
|
||||
import { DIST_DIR, PROJECT_ROOT } from "../utils/index.js";
|
||||
import { PROJECT_ROOT } from "../utils/index.js";
|
||||
|
||||
const separator = `\n${"-".repeat(40)}\n\n`;
|
||||
|
||||
@@ -120,7 +120,14 @@ function getLicenseText(dependencies) {
|
||||
return [head, content].join("\n\n");
|
||||
}
|
||||
|
||||
async function buildDependenciesLicense({ file, files, results, cliOptions }) {
|
||||
async function buildDependenciesLicense({
|
||||
packageConfig,
|
||||
file,
|
||||
results,
|
||||
cliOptions,
|
||||
}) {
|
||||
const { distDirectory, files } = packageConfig;
|
||||
|
||||
const fileName = file.output.file;
|
||||
|
||||
if (files.at(-1) !== file) {
|
||||
@@ -144,7 +151,7 @@ async function buildDependenciesLicense({ file, files, results, cliOptions }) {
|
||||
|
||||
const text = getLicenseText(dependencies);
|
||||
|
||||
await fs.writeFile(path.join(DIST_DIR, fileName), text);
|
||||
await fs.writeFile(path.join(distDirectory, fileName), text);
|
||||
}
|
||||
|
||||
export default buildDependenciesLicense;
|
||||
@@ -4,7 +4,7 @@ import browserslistToEsbuild from "browserslist-to-esbuild";
|
||||
import esbuild from "esbuild";
|
||||
import { nodeModulesPolyfillPlugin as esbuildPluginNodeModulePolyfills } from "esbuild-plugins-node-modules-polyfill";
|
||||
import createEsmUtils from "esm-utils";
|
||||
import { DIST_DIR, PROJECT_ROOT } from "../utils/index.js";
|
||||
import { PROJECT_ROOT } from "../utils/index.js";
|
||||
import esbuildPluginAddDefaultExport from "./esbuild-plugins/add-default-export.js";
|
||||
import esbuildPluginEvaluate from "./esbuild-plugins/evaluate.js";
|
||||
import esbuildPluginPrimitiveDefine from "./esbuild-plugins/primitive-define.js";
|
||||
@@ -35,7 +35,9 @@ const getRelativePath = (from, to) => {
|
||||
return relativePath;
|
||||
};
|
||||
|
||||
function getEsbuildOptions({ file, files, cliOptions }) {
|
||||
function getEsbuildOptions({ packageConfig, file, cliOptions }) {
|
||||
const { distDirectory, files } = packageConfig;
|
||||
|
||||
// Save dependencies to file
|
||||
file.dependencies = [];
|
||||
|
||||
@@ -232,7 +234,7 @@ function getEsbuildOptions({ file, files, cliOptions }) {
|
||||
target: [...(buildOptions.target ?? ["node14"])],
|
||||
logLevel: "error",
|
||||
format: file.output.format,
|
||||
outfile: path.join(DIST_DIR, cliOptions.saveAs ?? file.output.file),
|
||||
outfile: path.join(distDirectory, cliOptions.saveAs ?? file.output.file),
|
||||
// https://esbuild.github.io/api/#main-fields
|
||||
mainFields: file.platform === "node" ? ["module", "main"] : undefined,
|
||||
supported: {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import path from "node:path";
|
||||
import { DIST_DIR, PROJECT_ROOT, readJson, writeJson } from "../utils/index.js";
|
||||
import { PROJECT_ROOT, readJson, writeJson } from "../utils/index.js";
|
||||
|
||||
const keysToKeep = [
|
||||
"name",
|
||||
@@ -20,7 +20,8 @@ const keysToKeep = [
|
||||
"preferUnplugged",
|
||||
];
|
||||
|
||||
async function buildPackageJson({ file, files }) {
|
||||
async function buildPackageJson({ packageConfig, file }) {
|
||||
const { distDirectory, files } = packageConfig;
|
||||
const packageJson = await readJson(path.join(PROJECT_ROOT, file.input));
|
||||
|
||||
const bin = files.find(
|
||||
@@ -105,7 +106,7 @@ async function buildPackageJson({ file, files }) {
|
||||
};
|
||||
|
||||
await writeJson(
|
||||
path.join(DIST_DIR, file.output.file),
|
||||
path.join(distDirectory, file.output.file),
|
||||
Object.assign(pick(packageJson, keysToKeep), overrides),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ import path from "node:path";
|
||||
import url from "node:url";
|
||||
import { isValidIdentifier } from "@babel/types";
|
||||
import { outdent } from "outdent";
|
||||
import { DIST_DIR, PROJECT_ROOT, writeFile } from "../utils/index.js";
|
||||
import { PROJECT_ROOT, writeFile } from "../utils/index.js";
|
||||
|
||||
async function typesFileBuilder({ file }) {
|
||||
async function typesFileBuilder({ packageConfig, file }) {
|
||||
/**
|
||||
* @typedef {{ from: string, to: string }} ImportPathReplacement
|
||||
* @typedef {{ [input: string]: Array<ImportPathReplacement> }} ReplacementMap
|
||||
@@ -20,14 +20,17 @@ async function typesFileBuilder({ file }) {
|
||||
for (const { from, to } of replacements) {
|
||||
text = text.replaceAll(` from "${from}";`, ` from "${to}";`);
|
||||
}
|
||||
await writeFile(path.join(DIST_DIR, file.output.file), text);
|
||||
await writeFile(
|
||||
path.join(packageConfig.distDirectory, file.output.file),
|
||||
text,
|
||||
);
|
||||
}
|
||||
|
||||
function toPropertyKey(name) {
|
||||
return isValidIdentifier(name) ? name : JSON.stringify(name);
|
||||
}
|
||||
|
||||
async function buildPluginTypes({ file: { input, output } }) {
|
||||
async function buildPluginTypes({ packageConfig, file: { input, output } }) {
|
||||
const pluginModule = await import(
|
||||
url.pathToFileURL(path.join(PROJECT_ROOT, input))
|
||||
);
|
||||
@@ -52,7 +55,10 @@ async function buildPluginTypes({ file: { input, output } }) {
|
||||
};
|
||||
`;
|
||||
|
||||
await writeFile(path.join(DIST_DIR, output.file), `${code}\n`);
|
||||
await writeFile(
|
||||
path.join(packageConfig.distDirectory, output.file),
|
||||
`${code}\n`,
|
||||
);
|
||||
}
|
||||
|
||||
function buildTypes(options) {
|
||||
|
||||
+16
-8
@@ -7,7 +7,7 @@ import createEsmUtils from "esm-utils";
|
||||
import styleText from "node-style-text";
|
||||
import prettyBytes from "pretty-bytes";
|
||||
import { DIST_DIR } from "../utils/index.js";
|
||||
import files from "./config.js";
|
||||
import packageConfig from "./config.js";
|
||||
import parseArguments from "./parse-arguments.js";
|
||||
|
||||
const { require } = createEsmUtils(import.meta);
|
||||
@@ -44,7 +44,8 @@ const clear = () => {
|
||||
readline.cursorTo(process.stdout, 0, null);
|
||||
};
|
||||
|
||||
async function buildFile({ file, files, cliOptions, results }) {
|
||||
async function buildFile({ packageConfig, file, cliOptions, results }) {
|
||||
const { distDirectory } = packageConfig;
|
||||
let displayName = file.output.file;
|
||||
if (
|
||||
(file.platform === "universal" && file.output.format !== "esm") ||
|
||||
@@ -59,7 +60,9 @@ async function buildFile({ file, files, cliOptions, results }) {
|
||||
if (
|
||||
(cliOptions.files && !cliOptions.files.has(file.output.file)) ||
|
||||
(cliOptions.playground &&
|
||||
(file.output.format !== "umd" || file.output.file === "doc.js"))
|
||||
(file.output.format !== "esm" ||
|
||||
file.platform !== "universal" ||
|
||||
file.output.file === "doc.mjs"))
|
||||
) {
|
||||
console.log(status.SKIPPED);
|
||||
return;
|
||||
@@ -67,7 +70,7 @@ async function buildFile({ file, files, cliOptions, results }) {
|
||||
|
||||
let result;
|
||||
try {
|
||||
result = await file.build({ file, files, cliOptions, results });
|
||||
result = await file.build({ packageConfig, file, cliOptions, results });
|
||||
} catch (error) {
|
||||
console.log(status.FAIL + "\n");
|
||||
console.error(error);
|
||||
@@ -85,7 +88,7 @@ async function buildFile({ file, files, cliOptions, results }) {
|
||||
|
||||
const sizeMessages = [];
|
||||
if (cliOptions.printSize) {
|
||||
const { size } = await fs.stat(path.join(DIST_DIR, outputFile));
|
||||
const { size } = await fs.stat(path.join(distDirectory, outputFile));
|
||||
sizeMessages.push(prettyBytes(size));
|
||||
}
|
||||
|
||||
@@ -101,7 +104,7 @@ async function buildFile({ file, files, cliOptions, results }) {
|
||||
}
|
||||
|
||||
if (stableSize) {
|
||||
const { size } = await fs.stat(path.join(DIST_DIR, outputFile));
|
||||
const { size } = await fs.stat(path.join(distDirectory, outputFile));
|
||||
const sizeDiff = size - stableSize;
|
||||
const message = styleText[sizeDiff > 0 ? "yellow" : "green"](
|
||||
prettyBytes(sizeDiff),
|
||||
@@ -149,8 +152,13 @@ async function run() {
|
||||
console.log(styleText.inverse(" Building packages "));
|
||||
|
||||
const results = [];
|
||||
for (const file of files) {
|
||||
const result = await buildFile({ file, files, cliOptions, results });
|
||||
for (const file of packageConfig.files) {
|
||||
const result = await buildFile({
|
||||
packageConfig,
|
||||
file,
|
||||
cliOptions,
|
||||
results,
|
||||
});
|
||||
results.push(result);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-16
@@ -19,10 +19,10 @@ const {
|
||||
} = createEsmUtils(import.meta);
|
||||
const resolveEsmModulePath = (specifier) =>
|
||||
url.fileURLToPath(importMetaResolve(specifier));
|
||||
const copyFileBuilder = ({ file }) =>
|
||||
const copyFileBuilder = ({ packageConfig, file }) =>
|
||||
copyFile(
|
||||
path.join(PROJECT_ROOT, file.input),
|
||||
path.join(DIST_DIR, file.output.file),
|
||||
path.join(packageConfig.distDirectory, file.output.file),
|
||||
);
|
||||
|
||||
function getTypesFileConfig({ input: jsFileInput, outputBaseName, isPlugin }) {
|
||||
@@ -38,7 +38,7 @@ function getTypesFileConfig({ input: jsFileInput, outputBaseName, isPlugin }) {
|
||||
return {
|
||||
input,
|
||||
output: {
|
||||
file: outputBaseName + ".d.ts",
|
||||
file: `${outputBaseName}.d.ts`,
|
||||
},
|
||||
kind: "types",
|
||||
isPlugin,
|
||||
@@ -682,9 +682,10 @@ const nonPluginUniversalFiles = [
|
||||
return text;
|
||||
},
|
||||
},
|
||||
// Smaller size
|
||||
{
|
||||
module: require.resolve("chalk"),
|
||||
path: path.join(dirname, "./shims/chalk.cjs"),
|
||||
module: getPackageFile("picocolors/picocolors.browser.js"),
|
||||
path: path.join(dirname, "./shims/colors.js"),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -760,15 +761,6 @@ const nodejsFiles = [
|
||||
find: "const readBuffer = new Buffer(this.options.readChunk);",
|
||||
replacement: "const readBuffer = Buffer.alloc(this.options.readChunk);",
|
||||
},
|
||||
// `@babel/code-frame` and `@babel/highlight` use compatible `chalk`, but they installed separately
|
||||
{
|
||||
module: require.resolve("chalk", {
|
||||
paths: [require.resolve("@babel/highlight")],
|
||||
}),
|
||||
path: require.resolve("chalk", {
|
||||
paths: [require.resolve("@babel/code-frame")],
|
||||
}),
|
||||
},
|
||||
{
|
||||
module: getPackageFile("js-yaml/dist/js-yaml.mjs"),
|
||||
find: "var dump = dumper.dump;",
|
||||
@@ -808,7 +800,7 @@ const nodejsFiles = [
|
||||
{
|
||||
input: "src/cli/index.js",
|
||||
outputBaseName: "internal/cli",
|
||||
external: ["benchmark"],
|
||||
external: ["tinybench"],
|
||||
// TODO: Remove this when we drop support for Node.js v16
|
||||
replaceModule: [
|
||||
{
|
||||
@@ -876,4 +868,8 @@ const metaFiles = [
|
||||
|
||||
/** @type {Files[]} */
|
||||
const files = [...nodejsFiles, ...universalFiles, ...metaFiles].filter(Boolean);
|
||||
export default files;
|
||||
export default {
|
||||
packageName: "prettier",
|
||||
distDirectory: path.join(DIST_DIR, "prettier"),
|
||||
files,
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import path from "node:path";
|
||||
|
||||
const namespace = "with-default-export";
|
||||
const PLUGIN_NAMESPACE = "with-default-export";
|
||||
export default function esbuildPluginAddDefaultExport() {
|
||||
return {
|
||||
name: "addDefaultExport",
|
||||
@@ -10,31 +10,32 @@ export default function esbuildPluginAddDefaultExport() {
|
||||
return;
|
||||
}
|
||||
|
||||
let entry;
|
||||
|
||||
build.onResolve({ filter: /./u }, (module) => {
|
||||
build.onResolve({ filter: /./, namespace: "file" }, (module) => {
|
||||
if (module.kind === "entry-point") {
|
||||
const relativePath = module.path
|
||||
.slice(module.resolveDir.length + 1)
|
||||
const file = module.path;
|
||||
const relativePath = path
|
||||
.relative(module.resolveDir, file)
|
||||
.replaceAll("\\", "/");
|
||||
|
||||
entry = module.path;
|
||||
return { path: relativePath, namespace };
|
||||
return {
|
||||
path: relativePath,
|
||||
namespace: PLUGIN_NAMESPACE,
|
||||
pluginData: { file },
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
build.onLoad({ filter: /./u, namespace }, () => {
|
||||
const directory = path.dirname(entry);
|
||||
const source = `./${path.basename(entry)}`;
|
||||
build.onLoad({ filter: /./, namespace: PLUGIN_NAMESPACE }, (module) => {
|
||||
const { file } = module.pluginData;
|
||||
const source = JSON.stringify(`./${path.basename(file)}`);
|
||||
|
||||
return {
|
||||
contents: /* indent */ `
|
||||
import * as namespace from "${source}";
|
||||
import * as namespace from ${source};
|
||||
|
||||
export * from "${source}";
|
||||
export * from ${source};
|
||||
export default namespace;
|
||||
`,
|
||||
resolveDir: directory,
|
||||
resolveDir: path.dirname(file),
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1,41 +1,43 @@
|
||||
import url from "node:url";
|
||||
import { isValidIdentifier } from "@babel/types";
|
||||
import createEsmUtils from "esm-utils";
|
||||
import serialize from "serialize-javascript";
|
||||
|
||||
const { importModule } = createEsmUtils(import.meta);
|
||||
|
||||
export default function esbuildPluginEvaluate() {
|
||||
return {
|
||||
name: "evaluate",
|
||||
setup(build) {
|
||||
const { format } = build.initialOptions;
|
||||
|
||||
build.onLoad({ filter: /\.evaluate\.c?js$/u }, async ({ path }) => {
|
||||
const module = await importModule(path);
|
||||
const text = Object.entries(module)
|
||||
.map(([specifier, value]) => {
|
||||
const code =
|
||||
value instanceof RegExp
|
||||
? `/${value.source}/${value.flags}`
|
||||
: serialize(value, { space: 2 });
|
||||
build.onLoad(
|
||||
{ filter: /\.evaluate\.c?js$/, namespace: "file" },
|
||||
async ({ path }) => {
|
||||
const module = await import(url.pathToFileURL(path));
|
||||
const text = Object.entries(module)
|
||||
.filter(([specifier]) => specifier !== "module.exports")
|
||||
.map(([specifier, value]) => {
|
||||
const code =
|
||||
value instanceof RegExp
|
||||
? `/${value.source}/${value.flags}`
|
||||
: serialize(value, { space: 2 });
|
||||
|
||||
if (specifier === "default") {
|
||||
return format === "cjs"
|
||||
? `module.exports = ${code};`
|
||||
: `export default ${code};`;
|
||||
}
|
||||
|
||||
if (!isValidIdentifier(specifier)) {
|
||||
throw new Error(`${specifier} is not a valid specifier`);
|
||||
}
|
||||
|
||||
if (specifier === "default") {
|
||||
return format === "cjs"
|
||||
? `module.exports = ${code};`
|
||||
: `export default ${code};`;
|
||||
}
|
||||
|
||||
if (!isValidIdentifier(specifier)) {
|
||||
throw new Error(`${specifier} is not a valid specifier`);
|
||||
}
|
||||
|
||||
return format === "cjs"
|
||||
? `exports.${specifier} = ${code};`
|
||||
: `export const ${specifier} = ${code};`;
|
||||
})
|
||||
.join("\n");
|
||||
return { contents: text };
|
||||
});
|
||||
? `exports.${specifier} = ${code};`
|
||||
: `export const ${specifier} = ${code};`;
|
||||
})
|
||||
.join("\n");
|
||||
return { contents: text };
|
||||
},
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import fs from "node:fs/promises";
|
||||
|
||||
const DEFAULT_ON_RESOLVE_CONCEPTS = { filter: /./u, namespace: "file" };
|
||||
const DEFAULT_ON_RESOLVE_CONCEPTS = { filter: /./, namespace: "file" };
|
||||
const DEFAULT_ON_LOAD_CONCEPTS = {
|
||||
filter: /\.(?:js|json|mjs|cjs)$/u,
|
||||
filter: /\.(?:js|json|mjs|cjs)$/,
|
||||
namespace: "file",
|
||||
};
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ export default function esbuildPluginStripNodeProtocol() {
|
||||
return {
|
||||
name: "strip-node-protocol",
|
||||
setup(build) {
|
||||
build.onResolve({ filter: /^node:/u }, ({ path, kind }) =>
|
||||
build.resolve(path.slice(5), { kind }),
|
||||
build.onResolve(
|
||||
{ filter: /^node:/, namespace: "file" },
|
||||
({ path, kind }) => build.resolve(path.slice(5), { kind }),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -114,18 +114,6 @@ export default function esbuildPluginThrowWarnings({
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
warning.id === "package.json" &&
|
||||
warning.location.file.startsWith("node_modules/") &&
|
||||
(warning.text ===
|
||||
'The condition "default" here will never be used as it comes after both "import" and "require"' ||
|
||||
// `lines-and-columns`
|
||||
warning.text ===
|
||||
'The condition "types" here will never be used as it comes after both "import" and "require"')
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(warning);
|
||||
throw new Error(warning.text);
|
||||
}
|
||||
|
||||
@@ -121,6 +121,28 @@ function esmifyTypescriptEslint(text) {
|
||||
`,
|
||||
);
|
||||
|
||||
/**
|
||||
```js
|
||||
var FOO;
|
||||
(function (FOO) {
|
||||
})(FOO || (exports.FOO = FOO = {}));
|
||||
```
|
||||
->
|
||||
```js
|
||||
var FOO;
|
||||
(function (FOO) {
|
||||
})(FOO ?? {}));
|
||||
export {FOO};
|
||||
```
|
||||
*/
|
||||
text = text.replaceAll(
|
||||
/(?<=\n\}\))\((?<name>\S+) \|\| \(exports\.\k<name> = \k<name> = \{\}\)\);/gu,
|
||||
outdent`
|
||||
($<name> ??= {});
|
||||
export {$<name>};
|
||||
`,
|
||||
);
|
||||
|
||||
// Make sure ESBuild treat it as Module
|
||||
text += "\nexport {};";
|
||||
|
||||
|
||||
@@ -15,10 +15,9 @@ function parseArguments() {
|
||||
"save-as": { type: "string" },
|
||||
report: { type: "string", multiple: true },
|
||||
},
|
||||
strict: true,
|
||||
});
|
||||
|
||||
if (values.minify && values.noMinify) {
|
||||
if (values.minify && values["no-minify"]) {
|
||||
throw new Error("'--minify' and '--no-minify' can't be used together.");
|
||||
}
|
||||
|
||||
@@ -40,7 +39,9 @@ function parseArguments() {
|
||||
);
|
||||
}
|
||||
|
||||
if (!path.join(DIST_DIR, result.saveAs).startsWith(DIST_DIR)) {
|
||||
// TODO: Support package name
|
||||
const distDirectory = path.join(DIST_DIR, "prettier");
|
||||
if (!path.join(distDirectory, result.saveAs).startsWith(distDirectory)) {
|
||||
throw new Error("'--save-as' can only relative path");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const chalk = new Proxy(String, { get: () => chalk });
|
||||
|
||||
module.exports = chalk;
|
||||
@@ -0,0 +1,3 @@
|
||||
const colors = new Proxy(String, { get: () => colors });
|
||||
|
||||
export default colors;
|
||||
@@ -1,9 +1,10 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import path from "node:path";
|
||||
import minimist from "minimist";
|
||||
import { parseArgs } from "node:util";
|
||||
import semver from "semver";
|
||||
import {
|
||||
categories,
|
||||
changelogUnreleasedDirPath,
|
||||
changelogUnreleasedDirs,
|
||||
getEntries,
|
||||
@@ -11,11 +12,17 @@ import {
|
||||
replaceVersions,
|
||||
} from "./utils/changelog.js";
|
||||
|
||||
const { previousVersion, newVersion } = parseArgv();
|
||||
const { previousVersion, newVersion } = parseArguments();
|
||||
|
||||
const entries = changelogUnreleasedDirs.flatMap((dir) => {
|
||||
const dirPath = path.join(changelogUnreleasedDirPath, dir.name);
|
||||
return getEntries(dirPath);
|
||||
const { title } = categories.find((category) => category.dir === dir.name);
|
||||
|
||||
return getEntries(dirPath).map((entry) => {
|
||||
const content =
|
||||
entry.content.slice(0, 4) + ` ${title}:` + entry.content.slice(4);
|
||||
return { ...entry, content };
|
||||
});
|
||||
});
|
||||
|
||||
console.log(
|
||||
@@ -27,10 +34,15 @@ console.log(
|
||||
),
|
||||
);
|
||||
|
||||
function parseArgv() {
|
||||
const argv = minimist(process.argv.slice(2));
|
||||
const previousVersion = argv["prev-version"];
|
||||
const newVersion = argv["new-version"];
|
||||
function parseArguments() {
|
||||
const {
|
||||
values: { "prev-version": previousVersion, "new-version": newVersion },
|
||||
} = parseArgs({
|
||||
options: {
|
||||
"prev-version": { type: "string" },
|
||||
"new-version": { type: "string" },
|
||||
},
|
||||
});
|
||||
if (
|
||||
!previousVersion ||
|
||||
!newVersion ||
|
||||
|
||||
+20
-22
@@ -1,19 +1,13 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from "node:fs/promises";
|
||||
import { execa } from "execa";
|
||||
import spawn from "nano-spawn";
|
||||
|
||||
const CSPELL_CONFIG_FILE = new URL("../cspell.json", import.meta.url);
|
||||
const updateConfig = (config) =>
|
||||
fs.writeFile(CSPELL_CONFIG_FILE, JSON.stringify(config, undefined, 4) + "\n");
|
||||
const runSpellcheck = (options) => {
|
||||
const { yarnArgs, args, execaOptions } = {
|
||||
yarnArgs: [],
|
||||
args: [],
|
||||
...options,
|
||||
};
|
||||
return execa("yarn", [...yarnArgs, "lint:spellcheck", ...args], execaOptions);
|
||||
};
|
||||
const runSpellcheck = (args, options) =>
|
||||
spawn("yarn", ["lint:spellcheck", ...args], options);
|
||||
|
||||
console.log("Empty words ...");
|
||||
const config = JSON.parse(await fs.readFile(CSPELL_CONFIG_FILE));
|
||||
@@ -21,20 +15,24 @@ const original = config.words;
|
||||
await updateConfig({ ...config, words: [] });
|
||||
|
||||
console.log("Running spellcheck with empty words ...");
|
||||
const { stdout } = await runSpellcheck({
|
||||
args: ["--words-only", "--unique"],
|
||||
execaOptions: { reject: false },
|
||||
});
|
||||
let stdout = "";
|
||||
try {
|
||||
await runSpellcheck(["--words-only", "--unique"]);
|
||||
} catch (error) {
|
||||
({ stdout } = error);
|
||||
}
|
||||
|
||||
const words = stdout
|
||||
.split("\n")
|
||||
// Remove upper case word, if lower case one already exists
|
||||
.filter((word, _, words) => {
|
||||
const lowerCased = word.toLowerCase();
|
||||
return lowerCased === word || !words.includes(lowerCased);
|
||||
})
|
||||
// Compare function from https://github.com/streetsidesoftware/vscode-spell-checker/blob/2fde3bc5c658ee51da5a56580aa1370bf8174070/packages/client/src/settings/CSpellSettings.ts#L78
|
||||
.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
||||
? stdout
|
||||
.split("\n")
|
||||
// Remove upper case word, if lower case one already exists
|
||||
.filter((word, _, words) => {
|
||||
const lowerCased = word.toLowerCase();
|
||||
return lowerCased === word || !words.includes(lowerCased);
|
||||
})
|
||||
// Compare function from https://github.com/streetsidesoftware/vscode-spell-checker/blob/2fde3bc5c658ee51da5a56580aa1370bf8174070/packages/client/src/settings/CSpellSettings.ts#L78
|
||||
.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
|
||||
: [];
|
||||
config.words = words;
|
||||
|
||||
const removed = original.filter((word) => !words.includes(word));
|
||||
@@ -58,6 +56,6 @@ console.log("Updating words ...");
|
||||
await updateConfig(config);
|
||||
|
||||
console.log("Running spellcheck with new words ...");
|
||||
await runSpellcheck({ execaOptions: { stdout: "inherit" } });
|
||||
await runSpellcheck([], { stdio: "inherit" });
|
||||
|
||||
console.log("CSpell config file updated.");
|
||||
@@ -7,7 +7,7 @@ Remove revs without actual file change
|
||||
*/
|
||||
|
||||
import fs from "node:fs/promises";
|
||||
import { execa } from "execa";
|
||||
import spawn from "nano-spawn";
|
||||
|
||||
const FILE = new URL("../.git-blame-ignore-revs", import.meta.url);
|
||||
const content = await fs.readFile(FILE, "utf8");
|
||||
@@ -17,7 +17,7 @@ const revsToRemove = new Set();
|
||||
|
||||
const IGNORED_FILES = new Set(["package.json", "yarn.lock"]);
|
||||
async function getChangedFiles(rev) {
|
||||
const { stdout } = await execa("git", [
|
||||
const { stdout } = await spawn("git", [
|
||||
"show",
|
||||
"--name-only",
|
||||
"--pretty=",
|
||||
|
||||
@@ -6,6 +6,7 @@ import createEsmUtils from "esm-utils";
|
||||
import fg from "fast-glob";
|
||||
import semver from "semver";
|
||||
import {
|
||||
categories,
|
||||
changelogUnreleasedDirPath,
|
||||
changelogUnreleasedDirs,
|
||||
getEntries,
|
||||
@@ -36,28 +37,6 @@ const postFile = path.join(
|
||||
`${new Date().toISOString().replace(/T.+/u, "")}-${nextVersion}.md`,
|
||||
);
|
||||
|
||||
const categories = [
|
||||
{ dir: "javascript", title: "JavaScript" },
|
||||
{ dir: "typescript", title: "TypeScript" },
|
||||
{ dir: "flow", title: "Flow" },
|
||||
{ dir: "json", title: "JSON" },
|
||||
{ dir: "css", title: "CSS" },
|
||||
{ dir: "scss", title: "SCSS" },
|
||||
{ dir: "less", title: "Less" },
|
||||
{ dir: "html", title: "HTML" },
|
||||
{ dir: "vue", title: "Vue" },
|
||||
{ dir: "angular", title: "Angular" },
|
||||
{ dir: "lwc", title: "LWC" },
|
||||
{ dir: "handlebars", title: "Ember / Handlebars" },
|
||||
{ dir: "graphql", title: "GraphQL" },
|
||||
{ dir: "markdown", title: "Markdown" },
|
||||
{ dir: "mdx", title: "MDX" },
|
||||
{ dir: "yaml", title: "YAML" },
|
||||
{ dir: "api", title: "API" },
|
||||
{ dir: "cli", title: "CLI" },
|
||||
{ dir: "misc", title: "Miscellaneous" },
|
||||
];
|
||||
|
||||
const categoriesByDir = new Map(
|
||||
categories.map((category) => [category.dir, category]),
|
||||
);
|
||||
|
||||
@@ -134,6 +134,7 @@ function getSyntaxFromCategory(category) {
|
||||
case "angular":
|
||||
case "html":
|
||||
case "lwc":
|
||||
case "mjml":
|
||||
return "html";
|
||||
case "cli":
|
||||
return "sh";
|
||||
@@ -150,6 +151,7 @@ function getSyntaxFromCategory(category) {
|
||||
case "flow":
|
||||
case "javascript":
|
||||
case "api":
|
||||
case "misc":
|
||||
return "jsx";
|
||||
case "typescript":
|
||||
return "tsx";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import fs from "node:fs";
|
||||
import { LinesAndColumns } from "lines-and-columns";
|
||||
import indexToPosition from "index-to-position";
|
||||
import { outdent } from "outdent";
|
||||
import { CHANGELOG_CATEGORIES } from "./utils/changelog-categories.js";
|
||||
|
||||
@@ -142,9 +142,8 @@ for (const category of CHANGELOG_CATEGORIES) {
|
||||
function getCommentDescription(content, comment) {
|
||||
const start = content.indexOf(comment);
|
||||
const end = start + comment.length;
|
||||
const linesAndColumns = new LinesAndColumns(content);
|
||||
const [startLine, endLine] = [start, end].map(
|
||||
(index) => linesAndColumns.locationForIndex(index).line + 1,
|
||||
(index) => indexToPosition(content, index, { oneBased: true }).line,
|
||||
);
|
||||
|
||||
if (startLine === endLine) {
|
||||
|
||||
@@ -15,5 +15,5 @@
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"packageManager": "yarn@4.6.0"
|
||||
"packageManager": "yarn@4.9.0"
|
||||
}
|
||||
@@ -56,13 +56,17 @@ for (let step of [
|
||||
},
|
||||
params.manual ? steps.publishToNpm : steps.waitForBotRelease,
|
||||
steps.showInstructionsAfterNpmPublish,
|
||||
{
|
||||
name: "Merge release notes PR",
|
||||
process: steps.mergeBlogPost,
|
||||
},
|
||||
steps.updateDependentsCount,
|
||||
steps.bumpPrettier,
|
||||
{
|
||||
name: "Cleaning changelog",
|
||||
process: steps.cleanChangelog,
|
||||
skip: params.dry || params.next,
|
||||
},
|
||||
steps.bumpPrettier,
|
||||
steps.postPublishSteps,
|
||||
]) {
|
||||
if (typeof step === "function") {
|
||||
|
||||
@@ -11,10 +11,10 @@ export default async function generateBundles({ dry, version, manual }) {
|
||||
runYarn(["build", "--clean", "--print-size", "--compare-size"]),
|
||||
);
|
||||
|
||||
const builtPkg = await readJson("dist/package.json");
|
||||
const builtPkg = await readJson("dist/prettier/package.json");
|
||||
if (!dry && builtPkg.version !== version) {
|
||||
throw new Error(
|
||||
`Expected ${version} in dist/package.json but found ${builtPkg.version}`,
|
||||
`Expected ${version} in dist/prettier/package.json but found ${builtPkg.version}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ export { default as cleanChangelog } from "./clean-changelog.js";
|
||||
export { default as generateBundles } from "./generate-bundles.js";
|
||||
export { default as installDependencies } from "./install-dependencies.js";
|
||||
export { default as lintFiles } from "./lint-files.js";
|
||||
export { default as mergeBlogPost } from "./merge-blog-post.js";
|
||||
export { default as postPublishSteps } from "./post-publish-steps.js";
|
||||
export { default as publishToNpm } from "./publish-to-npm.js";
|
||||
export { default as pushToGit } from "./push-to-git.js";
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
import spawn from "nano-spawn";
|
||||
import fs from "node:fs/promises";
|
||||
import { runGit, runYarn } from "../utils.js";
|
||||
|
||||
export default async function installDependencies() {
|
||||
await spawn("rm", ["-rf", "node_modules"]);
|
||||
await runYarn(["install"]);
|
||||
const PROJECT_ROOT = new URL("../../../", import.meta.url);
|
||||
|
||||
await spawn("rm", ["-rf", "node_modules"], { cwd: "./website" });
|
||||
await runYarn(["install"], { cwd: "./website" });
|
||||
async function installDependenciesInDirectory(directory) {
|
||||
await fs.rm(new URL("./node_modules/", directory), {
|
||||
recursive: true,
|
||||
force: true,
|
||||
});
|
||||
await runYarn("install", { cwd: directory });
|
||||
}
|
||||
|
||||
export default async function installDependencies() {
|
||||
await Promise.all(
|
||||
[PROJECT_ROOT, new URL("./website/", PROJECT_ROOT)].map((directory) =>
|
||||
installDependenciesInDirectory(directory),
|
||||
),
|
||||
);
|
||||
|
||||
const { stdout: status } = await runGit(["ls-files", "-m"]);
|
||||
if (status) {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import styleText from "node-style-text";
|
||||
import semver from "semver";
|
||||
import { runGit, waitForEnter } from "../utils.js";
|
||||
|
||||
export default async function mergeBlogPost({
|
||||
dry,
|
||||
repo,
|
||||
version,
|
||||
previousVersion,
|
||||
}) {
|
||||
if (dry) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (semver.diff(version, previousVersion) === "patch") {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
styleText.yellow.bold("Please merge the release notes PR if exits."),
|
||||
);
|
||||
|
||||
await waitForEnter();
|
||||
await runGit(["pull", "--repo", repo]);
|
||||
}
|
||||
@@ -24,7 +24,7 @@ export default async function publishToNpm({ dry }) {
|
||||
args.push("--otp", otp);
|
||||
}
|
||||
|
||||
await spawn("npm", args, { cwd: "./dist" });
|
||||
await spawn("npm", args, { cwd: "./dist/prettier" });
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import styleText from "node-style-text";
|
||||
import { fetchText, logPromise, processFile, runGit } from "../utils.js";
|
||||
|
||||
async function update() {
|
||||
async function update({ repo }) {
|
||||
const npmPage = await logPromise(
|
||||
"Fetching npm dependents count",
|
||||
fetchText("https://www.npmjs.com/package/prettier"),
|
||||
@@ -56,7 +56,7 @@ async function update() {
|
||||
await logPromise("Committing and pushing to remote", async () => {
|
||||
await runGit(["add", "."]);
|
||||
await runGit(["commit", "-m", "Update dependents count"]);
|
||||
await runGit(["push"]);
|
||||
await runGit(["push", "--repo", repo]);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -71,13 +71,13 @@ function formatNumber(value) {
|
||||
return Math.floor(value / 1e5) / 10 + " million";
|
||||
}
|
||||
|
||||
export default async function updateDependentsCount({ dry, next }) {
|
||||
export default async function updateDependentsCount({ dry, next, repo }) {
|
||||
if (dry || next) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await update();
|
||||
await update({ repo });
|
||||
} catch (error) {
|
||||
console.log(styleText.red.bold(error.message));
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@ import { createRequire } from "node:module";
|
||||
import path from "node:path";
|
||||
import url from "node:url";
|
||||
import webpack from "webpack";
|
||||
import files from "../../build/config.js";
|
||||
import { DIST_DIR } from "../../utils/index.js";
|
||||
import packageConfig from "../../build/config.js";
|
||||
|
||||
function runWebpack(config) {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -30,6 +29,7 @@ const TEMPORARY_DIRECTORY = url.fileURLToPath(
|
||||
new URL("./.tmp", import.meta.url),
|
||||
);
|
||||
|
||||
const { distDirectory, files } = packageConfig;
|
||||
/* `require` in `parser-typescript.js`, #12338 */
|
||||
for (const file of files) {
|
||||
if (file.platform !== "universal") {
|
||||
@@ -39,7 +39,7 @@ for (const file of files) {
|
||||
|
||||
const stats = await runWebpack({
|
||||
mode: "production",
|
||||
entry: path.join(DIST_DIR, file.output.file),
|
||||
entry: path.join(distDirectory, file.output.file),
|
||||
output: {
|
||||
path: TEMPORARY_DIRECTORY,
|
||||
filename: `${file.output.file}.[contenthash:7].${
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
"scripts": {
|
||||
"test": "node ./index.js"
|
||||
},
|
||||
"packageManager": "yarn@4.6.0"
|
||||
"packageManager": "yarn@4.9.0"
|
||||
}
|
||||
@@ -11,5 +11,5 @@
|
||||
"test": "node test.js",
|
||||
"test-coverage": "npx nyc node test.js"
|
||||
},
|
||||
"packageManager": "yarn@4.6.0"
|
||||
"packageManager": "yarn@4.9.0"
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
const messageId = "print-function-parameter-order";
|
||||
const expectedParameters = ["path", "options", "print"];
|
||||
|
||||
export default {
|
||||
meta: {
|
||||
type: "suggestion",
|
||||
messages: {
|
||||
[messageId]:
|
||||
"`{{functionName}}` function parameters should in order of `path`, `options` and `print`.",
|
||||
},
|
||||
},
|
||||
create(context) {
|
||||
const sourceCode = context.getSourceCode();
|
||||
|
||||
return {
|
||||
":function[params.length>=3]"(node) {
|
||||
const parameterNames = node.params.map((node) =>
|
||||
node.type === "Identifier" ? node.name : "",
|
||||
);
|
||||
|
||||
// `embed` function order is `textToDoc, print, path, options`
|
||||
if (parameterNames.includes("textToDoc")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only if all three parameters exists
|
||||
if (expectedParameters.some((name) => !parameterNames.includes(name))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// In correct order
|
||||
if (
|
||||
expectedParameters.every(
|
||||
(name, index) => name === parameterNames[index],
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const firstToken = sourceCode.getFirstToken(node);
|
||||
const tokenBeforeBody = sourceCode.getTokenBefore(node.body);
|
||||
const functionName = node.id ? node.id.name : "print*";
|
||||
context.report({
|
||||
node,
|
||||
loc: {
|
||||
start: firstToken.loc.start,
|
||||
end: tokenBeforeBody.loc.end,
|
||||
},
|
||||
messageId,
|
||||
data: { functionName },
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -640,6 +640,31 @@ test("prefer-is-non-empty-array", {
|
||||
],
|
||||
});
|
||||
|
||||
test("print-function-parameter-order", {
|
||||
valid: [
|
||||
"function printFoo(path, options, print) {}",
|
||||
"function printFoo(path, opts, print) {}",
|
||||
"function printFoo(path, options, printPath) {}",
|
||||
"function printFoo() {}",
|
||||
"function printFoo(path, print) {}",
|
||||
"function printFoo(path, options) {}",
|
||||
"function printFoo(print, options) {}",
|
||||
"function embed(path, print, textToDoc, options) {}",
|
||||
],
|
||||
invalid: [
|
||||
"function printFoo(path, print, options) {}",
|
||||
"function printFoo(path, print, options, args) {}",
|
||||
"function printFoo(options, print, path) {}",
|
||||
"const printFoo = function (path, print, options) {}",
|
||||
"const printFoo = function printFoo(path, print, options) {}",
|
||||
"const printFoo = (path, print, options) => {}",
|
||||
].map((code) => ({
|
||||
code,
|
||||
output: null,
|
||||
errors: 1,
|
||||
})),
|
||||
});
|
||||
|
||||
test("no-empty-flat-contents-for-if-break", {
|
||||
valid: [
|
||||
"ifBreak('foo', 'bar')",
|
||||
|
||||
@@ -11,6 +11,7 @@ export const CHANGELOG_CATEGORIES = [
|
||||
"json",
|
||||
"less",
|
||||
"lwc",
|
||||
"mjml",
|
||||
"markdown",
|
||||
"mdx",
|
||||
"scss",
|
||||
|
||||
@@ -10,6 +10,29 @@ export const changelogUnreleasedDirPath = path.join(
|
||||
"../../changelog_unreleased",
|
||||
);
|
||||
|
||||
export const categories = [
|
||||
{ dir: "javascript", title: "JavaScript" },
|
||||
{ dir: "typescript", title: "TypeScript" },
|
||||
{ dir: "flow", title: "Flow" },
|
||||
{ dir: "json", title: "JSON" },
|
||||
{ dir: "css", title: "CSS" },
|
||||
{ dir: "scss", title: "SCSS" },
|
||||
{ dir: "less", title: "Less" },
|
||||
{ dir: "html", title: "HTML" },
|
||||
{ dir: "vue", title: "Vue" },
|
||||
{ dir: "angular", title: "Angular" },
|
||||
{ dir: "lwc", title: "LWC" },
|
||||
{ dir: "mjml", title: "MJML" },
|
||||
{ dir: "handlebars", title: "Ember / Handlebars" },
|
||||
{ dir: "graphql", title: "GraphQL" },
|
||||
{ dir: "markdown", title: "Markdown" },
|
||||
{ dir: "mdx", title: "MDX" },
|
||||
{ dir: "yaml", title: "YAML" },
|
||||
{ dir: "api", title: "API" },
|
||||
{ dir: "cli", title: "CLI" },
|
||||
{ dir: "misc", title: "Miscellaneous" },
|
||||
];
|
||||
|
||||
export const changelogUnreleasedDirs = fs
|
||||
.readdirSync(changelogUnreleasedDirPath, {
|
||||
withFileTypes: true,
|
||||
|
||||
Reference in new issue
Block a user