@benjavicente/router-utils 1.161.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021-present Tanner Linsley
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,23 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+ //#endregion
23
+ exports.__toESM = __toESM;
@@ -0,0 +1,104 @@
1
+ const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
2
+ let _babel_parser = require("@babel/parser");
3
+ let _babel_generator = require("@babel/generator");
4
+ _babel_generator = require_runtime.__toESM(_babel_generator);
5
+ let _babel_types = require("@babel/types");
6
+ _babel_types = require_runtime.__toESM(_babel_types);
7
+ let babel_dead_code_elimination = require("babel-dead-code-elimination");
8
+ //#region src/ast.ts
9
+ function parseAst({ code, ...opts }) {
10
+ return (0, _babel_parser.parse)(code, {
11
+ plugins: [
12
+ "jsx",
13
+ "typescript",
14
+ "explicitResourceManagement",
15
+ "decorators"
16
+ ],
17
+ sourceType: "module",
18
+ ...opts
19
+ });
20
+ }
21
+ var generate = _babel_generator.default;
22
+ if ("default" in generate) generate = generate.default;
23
+ function generateFromAst(ast, opts) {
24
+ return generate(ast, opts ? {
25
+ importAttributesKeyword: "with",
26
+ sourceMaps: true,
27
+ ...opts
28
+ } : void 0);
29
+ }
30
+ /**
31
+ * Strips TypeScript type-only exports and imports from an AST.
32
+ *
33
+ * This is necessary because babel-dead-code-elimination doesn't handle
34
+ * TypeScript type exports/imports. When a type export references an import
35
+ * that pulls in server-only code, the dead code elimination won't remove
36
+ * that import because it sees the type as still referencing it.
37
+ *
38
+ * This function removes:
39
+ * - `export type Foo = ...`
40
+ * - `export interface Foo { ... }`
41
+ * - `export type { Foo } from './module'`
42
+ * - `export type * from './module'`
43
+ * - Type specifiers in mixed exports: `export { value, type Foo }` -> `export { value }`
44
+ * - `import type { Foo } from './module'`
45
+ * - Type specifiers in mixed imports: `import { value, type Foo } from './module'` -> `import { value }`
46
+ *
47
+ * Note: Non-exported type/interface declarations are preserved as they may be
48
+ * used as type annotations within the code.
49
+ *
50
+ * @param ast - The Babel AST (or ParseResult) to mutate
51
+ */
52
+ function stripTypeExports(ast) {
53
+ ast.program.body = ast.program.body.filter((node) => {
54
+ if (_babel_types.isExportNamedDeclaration(node)) {
55
+ if (node.exportKind === "type") return false;
56
+ if (node.specifiers.length > 0) {
57
+ node.specifiers = node.specifiers.filter((specifier) => {
58
+ if (_babel_types.isExportSpecifier(specifier)) return specifier.exportKind !== "type";
59
+ return true;
60
+ });
61
+ if (node.specifiers.length === 0 && !node.declaration) return false;
62
+ }
63
+ }
64
+ if (_babel_types.isExportAllDeclaration(node)) {
65
+ if (node.exportKind === "type") return false;
66
+ }
67
+ if (_babel_types.isImportDeclaration(node)) {
68
+ if (node.importKind === "type") return false;
69
+ if (node.specifiers.length > 0) {
70
+ node.specifiers = node.specifiers.filter((specifier) => {
71
+ if (_babel_types.isImportSpecifier(specifier)) return specifier.importKind !== "type";
72
+ return true;
73
+ });
74
+ if (node.specifiers.length === 0) return false;
75
+ }
76
+ }
77
+ return true;
78
+ });
79
+ }
80
+ /**
81
+ * Performs dead code elimination on the AST, with TypeScript type stripping.
82
+ *
83
+ * This is a wrapper around babel-dead-code-elimination that first strips
84
+ * TypeScript type-only exports and imports. This is necessary because
85
+ * babel-dead-code-elimination doesn't handle type exports, which can cause
86
+ * imports to be retained when they're only referenced by type exports.
87
+ *
88
+ * @param ast - The Babel AST to mutate
89
+ * @param candidates - Optional set of identifier paths to consider for removal.
90
+ * If provided, only these identifiers will be candidates for removal.
91
+ * This should be the result of `findReferencedIdentifiers(ast)` called
92
+ * before any AST transformations.
93
+ */
94
+ function deadCodeElimination(ast, candidates) {
95
+ stripTypeExports(ast);
96
+ (0, babel_dead_code_elimination.deadCodeElimination)(ast, candidates);
97
+ }
98
+ //#endregion
99
+ exports.deadCodeElimination = deadCodeElimination;
100
+ exports.generateFromAst = generateFromAst;
101
+ exports.parseAst = parseAst;
102
+ exports.stripTypeExports = stripTypeExports;
103
+
104
+ //# sourceMappingURL=ast.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.cjs","names":[],"sources":["../../src/ast.ts"],"sourcesContent":["import { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nimport * as t from '@babel/types'\nimport {\n deadCodeElimination as _deadCodeElimination,\n findReferencedIdentifiers,\n} from 'babel-dead-code-elimination'\nimport type { GeneratorOptions, GeneratorResult } from '@babel/generator'\nimport type { ParseResult, ParserOptions } from '@babel/parser'\nimport type * as _babel_types from '@babel/types'\n\nexport type ParseAstOptions = ParserOptions & {\n code: string\n}\n\nexport type ParseAstResult = ParseResult<_babel_types.File>\nexport function parseAst({ code, ...opts }: ParseAstOptions): ParseAstResult {\n return parse(code, {\n plugins: [\n 'jsx',\n 'typescript',\n 'explicitResourceManagement',\n 'decorators', // required for Angular and other decorator-based code\n ],\n sourceType: 'module',\n ...opts,\n })\n}\n\nlet generate = _generate\n\nif ('default' in generate) {\n generate = generate.default as typeof generate\n}\ntype GenerateFromAstOptions = GeneratorOptions &\n Required<Pick<GeneratorOptions, 'sourceFileName' | 'filename'>>\nexport function generateFromAst(\n ast: _babel_types.Node,\n opts?: GenerateFromAstOptions,\n): GeneratorResult {\n return generate(\n ast,\n opts\n ? { importAttributesKeyword: 'with', sourceMaps: true, ...opts }\n : undefined,\n )\n}\nexport type { GeneratorResult } from '@babel/generator'\n\n/**\n * Strips TypeScript type-only exports and imports from an AST.\n *\n * This is necessary because babel-dead-code-elimination doesn't handle\n * TypeScript type exports/imports. When a type export references an import\n * that pulls in server-only code, the dead code elimination won't remove\n * that import because it sees the type as still referencing it.\n *\n * This function removes:\n * - `export type Foo = ...`\n * - `export interface Foo { ... }`\n * - `export type { Foo } from './module'`\n * - `export type * from './module'`\n * - Type specifiers in mixed exports: `export { value, type Foo }` -> `export { value }`\n * - `import type { Foo } from './module'`\n * - Type specifiers in mixed imports: `import { value, type Foo } from './module'` -> `import { value }`\n *\n * Note: Non-exported type/interface declarations are preserved as they may be\n * used as type annotations within the code.\n *\n * @param ast - The Babel AST (or ParseResult) to mutate\n */\nexport function stripTypeExports(ast: ParseResult<_babel_types.File>): void {\n // Filter the program body to remove type-only nodes\n ast.program.body = ast.program.body.filter((node) => {\n // Handle export declarations\n if (t.isExportNamedDeclaration(node)) {\n // Remove entire export if it's a type-only export\n // e.g., `export type Foo = string`, `export interface Bar {}`, `export type { X } from './y'`\n if (node.exportKind === 'type') {\n return false\n }\n\n // For value exports with mixed specifiers, filter out type-only specifiers\n // e.g., `export { value, type TypeOnly }` -> `export { value }`\n if (node.specifiers.length > 0) {\n node.specifiers = node.specifiers.filter((specifier) => {\n if (t.isExportSpecifier(specifier)) {\n return specifier.exportKind !== 'type'\n }\n return true\n })\n\n // If all specifiers were removed, remove the entire export declaration\n // (unless it has a declaration like `export const x = 1`)\n if (node.specifiers.length === 0 && !node.declaration) {\n return false\n }\n }\n }\n\n // Handle type-only export-all declarations\n // e.g., `export type * from './module'`\n if (t.isExportAllDeclaration(node)) {\n if (node.exportKind === 'type') {\n return false\n }\n }\n\n // Handle import declarations\n if (t.isImportDeclaration(node)) {\n // Remove entire import if it's a type-only import\n // e.g., `import type { Foo } from './module'`\n if (node.importKind === 'type') {\n return false\n }\n\n // For value imports with mixed specifiers, filter out type-only specifiers\n // e.g., `import { value, type TypeOnly } from './module'` -> `import { value }`\n if (node.specifiers.length > 0) {\n node.specifiers = node.specifiers.filter((specifier) => {\n if (t.isImportSpecifier(specifier)) {\n return specifier.importKind !== 'type'\n }\n return true\n })\n\n // If all specifiers were removed, remove the entire import declaration\n if (node.specifiers.length === 0) {\n return false\n }\n }\n }\n\n return true\n })\n}\n\n// Re-export findReferencedIdentifiers from babel-dead-code-elimination\nexport { findReferencedIdentifiers }\n\n/**\n * Performs dead code elimination on the AST, with TypeScript type stripping.\n *\n * This is a wrapper around babel-dead-code-elimination that first strips\n * TypeScript type-only exports and imports. This is necessary because\n * babel-dead-code-elimination doesn't handle type exports, which can cause\n * imports to be retained when they're only referenced by type exports.\n *\n * @param ast - The Babel AST to mutate\n * @param candidates - Optional set of identifier paths to consider for removal.\n * If provided, only these identifiers will be candidates for removal.\n * This should be the result of `findReferencedIdentifiers(ast)` called\n * before any AST transformations.\n */\nexport function deadCodeElimination(\n ast: ParseResult<_babel_types.File>,\n candidates?: ReturnType<typeof findReferencedIdentifiers>,\n): void {\n // First strip TypeScript type-only exports and imports\n stripTypeExports(ast)\n\n // Then run the original dead code elimination\n _deadCodeElimination(ast, candidates)\n}\n"],"mappings":";;;;;;;;AAgBA,SAAgB,SAAS,EAAE,MAAM,GAAG,QAAyC;AAC3E,SAAA,GAAA,cAAA,OAAa,MAAM;EACjB,SAAS;GACP;GACA;GACA;GACA;GACD;EACD,YAAY;EACZ,GAAG;EACJ,CAAC;;AAGJ,IAAI,WAAW,iBAAA;AAEf,IAAI,aAAa,SACf,YAAW,SAAS;AAItB,SAAgB,gBACd,KACA,MACiB;AACjB,QAAO,SACL,KACA,OACI;EAAE,yBAAyB;EAAQ,YAAY;EAAM,GAAG;EAAM,GAC9D,KAAA,EACL;;;;;;;;;;;;;;;;;;;;;;;;AA0BH,SAAgB,iBAAiB,KAA2C;AAE1E,KAAI,QAAQ,OAAO,IAAI,QAAQ,KAAK,QAAQ,SAAS;AAEnD,MAAI,aAAE,yBAAyB,KAAK,EAAE;AAGpC,OAAI,KAAK,eAAe,OACtB,QAAO;AAKT,OAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,SAAK,aAAa,KAAK,WAAW,QAAQ,cAAc;AACtD,SAAI,aAAE,kBAAkB,UAAU,CAChC,QAAO,UAAU,eAAe;AAElC,YAAO;MACP;AAIF,QAAI,KAAK,WAAW,WAAW,KAAK,CAAC,KAAK,YACxC,QAAO;;;AAOb,MAAI,aAAE,uBAAuB,KAAK;OAC5B,KAAK,eAAe,OACtB,QAAO;;AAKX,MAAI,aAAE,oBAAoB,KAAK,EAAE;AAG/B,OAAI,KAAK,eAAe,OACtB,QAAO;AAKT,OAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,SAAK,aAAa,KAAK,WAAW,QAAQ,cAAc;AACtD,SAAI,aAAE,kBAAkB,UAAU,CAChC,QAAO,UAAU,eAAe;AAElC,YAAO;MACP;AAGF,QAAI,KAAK,WAAW,WAAW,EAC7B,QAAO;;;AAKb,SAAO;GACP;;;;;;;;;;;;;;;;AAoBJ,SAAgB,oBACd,KACA,YACM;AAEN,kBAAiB,IAAI;AAGrB,EAAA,GAAA,4BAAA,qBAAqB,KAAK,WAAW"}
@@ -0,0 +1,51 @@
1
+ import { findReferencedIdentifiers } from 'babel-dead-code-elimination';
2
+ import { GeneratorOptions, GeneratorResult } from '@babel/generator';
3
+ import { ParseResult, ParserOptions } from '@babel/parser';
4
+ import type * as _babel_types from '@babel/types';
5
+ export type ParseAstOptions = ParserOptions & {
6
+ code: string;
7
+ };
8
+ export type ParseAstResult = ParseResult<_babel_types.File>;
9
+ export declare function parseAst({ code, ...opts }: ParseAstOptions): ParseAstResult;
10
+ type GenerateFromAstOptions = GeneratorOptions & Required<Pick<GeneratorOptions, 'sourceFileName' | 'filename'>>;
11
+ export declare function generateFromAst(ast: _babel_types.Node, opts?: GenerateFromAstOptions): GeneratorResult;
12
+ export type { GeneratorResult } from '@babel/generator';
13
+ /**
14
+ * Strips TypeScript type-only exports and imports from an AST.
15
+ *
16
+ * This is necessary because babel-dead-code-elimination doesn't handle
17
+ * TypeScript type exports/imports. When a type export references an import
18
+ * that pulls in server-only code, the dead code elimination won't remove
19
+ * that import because it sees the type as still referencing it.
20
+ *
21
+ * This function removes:
22
+ * - `export type Foo = ...`
23
+ * - `export interface Foo { ... }`
24
+ * - `export type { Foo } from './module.cjs'`
25
+ * - `export type * from './module.cjs'`
26
+ * - Type specifiers in mixed exports: `export { value, type Foo }` -> `export { value }`
27
+ * - `import type { Foo } from './module.cjs'`
28
+ * - Type specifiers in mixed imports: `import { value, type Foo } from './module.cjs'` -> `import { value }`
29
+ *
30
+ * Note: Non-exported type/interface declarations are preserved as they may be
31
+ * used as type annotations within the code.
32
+ *
33
+ * @param ast - The Babel AST (or ParseResult) to mutate
34
+ */
35
+ export declare function stripTypeExports(ast: ParseResult<_babel_types.File>): void;
36
+ export { findReferencedIdentifiers };
37
+ /**
38
+ * Performs dead code elimination on the AST, with TypeScript type stripping.
39
+ *
40
+ * This is a wrapper around babel-dead-code-elimination that first strips
41
+ * TypeScript type-only exports and imports. This is necessary because
42
+ * babel-dead-code-elimination doesn't handle type exports, which can cause
43
+ * imports to be retained when they're only referenced by type exports.
44
+ *
45
+ * @param ast - The Babel AST to mutate
46
+ * @param candidates - Optional set of identifier paths to consider for removal.
47
+ * If provided, only these identifiers will be candidates for removal.
48
+ * This should be the result of `findReferencedIdentifiers(ast)` called
49
+ * before any AST transformations.
50
+ */
51
+ export declare function deadCodeElimination(ast: ParseResult<_babel_types.File>, candidates?: ReturnType<typeof findReferencedIdentifiers>): void;
@@ -0,0 +1,24 @@
1
+ require("./_virtual/_rolldown/runtime.cjs");
2
+ let node_fs_promises = require("node:fs/promises");
3
+ let pathe = require("pathe");
4
+ let tinyglobby = require("tinyglobby");
5
+ //#region src/copy-files-plugin.ts
6
+ function copyFilesPlugin({ fromDir, toDir, pattern = "**" }) {
7
+ return {
8
+ name: "copy-files",
9
+ async writeBundle() {
10
+ const entries = await (0, tinyglobby.glob)(pattern, { cwd: fromDir });
11
+ if (entries.length === 0) throw new Error(`No files found matching pattern "${pattern}" in directory "${fromDir}"`);
12
+ for (const entry of entries) {
13
+ const srcPath = (0, pathe.join)(fromDir, entry);
14
+ const destPath = (0, pathe.join)(toDir, entry);
15
+ await (0, node_fs_promises.mkdir)((0, pathe.dirname)(destPath), { recursive: true });
16
+ await (0, node_fs_promises.copyFile)(srcPath, destPath);
17
+ }
18
+ }
19
+ };
20
+ }
21
+ //#endregion
22
+ exports.copyFilesPlugin = copyFilesPlugin;
23
+
24
+ //# sourceMappingURL=copy-files-plugin.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy-files-plugin.cjs","names":[],"sources":["../../src/copy-files-plugin.ts"],"sourcesContent":["import { copyFile, mkdir } from 'node:fs/promises'\nimport { dirname, join } from 'pathe'\nimport { glob } from 'tinyglobby'\nimport type { Plugin } from 'vite'\n\nexport function copyFilesPlugin({\n fromDir,\n toDir,\n pattern = '**',\n}: {\n pattern?: string | Array<string>\n fromDir: string\n toDir: string\n}): Plugin {\n return {\n name: 'copy-files',\n async writeBundle() {\n const entries = await glob(pattern, { cwd: fromDir })\n if (entries.length === 0) {\n throw new Error(\n `No files found matching pattern \"${pattern}\" in directory \"${fromDir}\"`,\n )\n }\n\n for (const entry of entries) {\n const srcPath = join(fromDir, entry)\n const destPath = join(toDir, entry)\n // Ensure the destination directory exists\n await mkdir(dirname(destPath), { recursive: true })\n await copyFile(srcPath, destPath)\n }\n },\n }\n}\n"],"mappings":";;;;;AAKA,SAAgB,gBAAgB,EAC9B,SACA,OACA,UAAU,QAKD;AACT,QAAO;EACL,MAAM;EACN,MAAM,cAAc;GAClB,MAAM,UAAU,OAAA,GAAA,WAAA,MAAW,SAAS,EAAE,KAAK,SAAS,CAAC;AACrD,OAAI,QAAQ,WAAW,EACrB,OAAM,IAAI,MACR,oCAAoC,QAAQ,kBAAkB,QAAQ,GACvE;AAGH,QAAK,MAAM,SAAS,SAAS;IAC3B,MAAM,WAAA,GAAA,MAAA,MAAe,SAAS,MAAM;IACpC,MAAM,YAAA,GAAA,MAAA,MAAgB,OAAO,MAAM;AAEnC,WAAA,GAAA,iBAAA,QAAA,GAAA,MAAA,SAAoB,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;AACnD,WAAA,GAAA,iBAAA,UAAe,SAAS,SAAS;;;EAGtC"}
@@ -0,0 +1,6 @@
1
+ import { Plugin } from 'vite';
2
+ export declare function copyFilesPlugin({ fromDir, toDir, pattern, }: {
3
+ pattern?: string | Array<string>;
4
+ fromDir: string;
5
+ toDir: string;
6
+ }): Plugin;
@@ -0,0 +1,13 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("./_virtual/_rolldown/runtime.cjs");
3
+ const require_ast = require("./ast.cjs");
4
+ const require_logger = require("./logger.cjs");
5
+ const require_copy_files_plugin = require("./copy-files-plugin.cjs");
6
+ let babel_dead_code_elimination = require("babel-dead-code-elimination");
7
+ exports.copyFilesPlugin = require_copy_files_plugin.copyFilesPlugin;
8
+ exports.deadCodeElimination = require_ast.deadCodeElimination;
9
+ exports.findReferencedIdentifiers = babel_dead_code_elimination.findReferencedIdentifiers;
10
+ exports.generateFromAst = require_ast.generateFromAst;
11
+ exports.logDiff = require_logger.logDiff;
12
+ exports.parseAst = require_ast.parseAst;
13
+ exports.stripTypeExports = require_ast.stripTypeExports;
@@ -0,0 +1,4 @@
1
+ export { parseAst, generateFromAst, deadCodeElimination, findReferencedIdentifiers, stripTypeExports, } from './ast.cjs';
2
+ export type { ParseAstOptions, ParseAstResult, GeneratorResult } from './ast.cjs';
3
+ export { logDiff } from './logger.cjs';
4
+ export { copyFilesPlugin } from './copy-files-plugin.cjs';
@@ -0,0 +1,50 @@
1
+ const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
2
+ let ansis = require("ansis");
3
+ ansis = require_runtime.__toESM(ansis);
4
+ let diff = require("diff");
5
+ //#region src/logger.ts
6
+ function logDiff(oldStr, newStr) {
7
+ const differences = (0, diff.diffWords)(oldStr, newStr);
8
+ let output = "";
9
+ let unchangedLines = "";
10
+ function processUnchangedLines(lines) {
11
+ const lineArray = lines.split("\n");
12
+ if (lineArray.length > 4) return [
13
+ ansis.default.dim(lineArray[0]),
14
+ ansis.default.dim(lineArray[1]),
15
+ "",
16
+ ansis.default.dim.bold(`... (${lineArray.length - 4} lines) ...`),
17
+ "",
18
+ ansis.default.dim(lineArray[lineArray.length - 2]),
19
+ ansis.default.dim(lineArray[lineArray.length - 1])
20
+ ].join("\n");
21
+ return ansis.default.dim(lines);
22
+ }
23
+ differences.forEach((part, index) => {
24
+ const nextPart = differences[index + 1];
25
+ if (part.added) {
26
+ if (unchangedLines) {
27
+ output += processUnchangedLines(unchangedLines);
28
+ unchangedLines = "";
29
+ }
30
+ output += ansis.default.green.bold(part.value);
31
+ if (nextPart?.removed) output += " ";
32
+ } else if (part.removed) {
33
+ if (unchangedLines) {
34
+ output += processUnchangedLines(unchangedLines);
35
+ unchangedLines = "";
36
+ }
37
+ output += ansis.default.red.bold(part.value);
38
+ if (nextPart?.added) output += " ";
39
+ } else unchangedLines += part.value;
40
+ });
41
+ if (unchangedLines) output += processUnchangedLines(unchangedLines);
42
+ if (output) {
43
+ console.log("\nDiff:");
44
+ console.log(output + "\n\n");
45
+ } else console.log("No changes");
46
+ }
47
+ //#endregion
48
+ exports.logDiff = logDiff;
49
+
50
+ //# sourceMappingURL=logger.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.cjs","names":[],"sources":["../../src/logger.ts"],"sourcesContent":["import ansis from 'ansis'\nimport { diffWords } from 'diff'\n\nexport function logDiff(oldStr: string, newStr: string) {\n const differences = diffWords(oldStr, newStr)\n\n let output = ''\n let unchangedLines = ''\n\n function processUnchangedLines(lines: string): string {\n const lineArray = lines.split('\\n')\n if (lineArray.length > 4) {\n return [\n ansis.dim(lineArray[0]),\n ansis.dim(lineArray[1]),\n '',\n ansis.dim.bold(`... (${lineArray.length - 4} lines) ...`),\n '',\n ansis.dim(lineArray[lineArray.length - 2]),\n ansis.dim(lineArray[lineArray.length - 1]),\n ].join('\\n')\n }\n return ansis.dim(lines)\n }\n\n differences.forEach((part, index) => {\n const nextPart = differences[index + 1]\n\n if (part.added) {\n if (unchangedLines) {\n output += processUnchangedLines(unchangedLines)\n unchangedLines = ''\n }\n output += ansis.green.bold(part.value)\n if (nextPart?.removed) output += ' '\n } else if (part.removed) {\n if (unchangedLines) {\n output += processUnchangedLines(unchangedLines)\n unchangedLines = ''\n }\n output += ansis.red.bold(part.value)\n if (nextPart?.added) output += ' '\n } else {\n unchangedLines += part.value\n }\n })\n\n // Process any remaining unchanged lines at the end\n if (unchangedLines) {\n output += processUnchangedLines(unchangedLines)\n }\n\n if (output) {\n console.log('\\nDiff:')\n console.log(output + '\\n\\n')\n } else {\n console.log('No changes')\n }\n}\n"],"mappings":";;;;;AAGA,SAAgB,QAAQ,QAAgB,QAAgB;CACtD,MAAM,eAAA,GAAA,KAAA,WAAwB,QAAQ,OAAO;CAE7C,IAAI,SAAS;CACb,IAAI,iBAAiB;CAErB,SAAS,sBAAsB,OAAuB;EACpD,MAAM,YAAY,MAAM,MAAM,KAAK;AACnC,MAAI,UAAU,SAAS,EACrB,QAAO;GACL,MAAA,QAAM,IAAI,UAAU,GAAG;GACvB,MAAA,QAAM,IAAI,UAAU,GAAG;GACvB;GACA,MAAA,QAAM,IAAI,KAAK,QAAQ,UAAU,SAAS,EAAE,aAAa;GACzD;GACA,MAAA,QAAM,IAAI,UAAU,UAAU,SAAS,GAAG;GAC1C,MAAA,QAAM,IAAI,UAAU,UAAU,SAAS,GAAG;GAC3C,CAAC,KAAK,KAAK;AAEd,SAAO,MAAA,QAAM,IAAI,MAAM;;AAGzB,aAAY,SAAS,MAAM,UAAU;EACnC,MAAM,WAAW,YAAY,QAAQ;AAErC,MAAI,KAAK,OAAO;AACd,OAAI,gBAAgB;AAClB,cAAU,sBAAsB,eAAe;AAC/C,qBAAiB;;AAEnB,aAAU,MAAA,QAAM,MAAM,KAAK,KAAK,MAAM;AACtC,OAAI,UAAU,QAAS,WAAU;aACxB,KAAK,SAAS;AACvB,OAAI,gBAAgB;AAClB,cAAU,sBAAsB,eAAe;AAC/C,qBAAiB;;AAEnB,aAAU,MAAA,QAAM,IAAI,KAAK,KAAK,MAAM;AACpC,OAAI,UAAU,MAAO,WAAU;QAE/B,mBAAkB,KAAK;GAEzB;AAGF,KAAI,eACF,WAAU,sBAAsB,eAAe;AAGjD,KAAI,QAAQ;AACV,UAAQ,IAAI,UAAU;AACtB,UAAQ,IAAI,SAAS,OAAO;OAE5B,SAAQ,IAAI,aAAa"}
@@ -0,0 +1 @@
1
+ export declare function logDiff(oldStr: string, newStr: string): void;
@@ -0,0 +1,51 @@
1
+ import { findReferencedIdentifiers } from 'babel-dead-code-elimination';
2
+ import { GeneratorOptions, GeneratorResult } from '@babel/generator';
3
+ import { ParseResult, ParserOptions } from '@babel/parser';
4
+ import type * as _babel_types from '@babel/types';
5
+ export type ParseAstOptions = ParserOptions & {
6
+ code: string;
7
+ };
8
+ export type ParseAstResult = ParseResult<_babel_types.File>;
9
+ export declare function parseAst({ code, ...opts }: ParseAstOptions): ParseAstResult;
10
+ type GenerateFromAstOptions = GeneratorOptions & Required<Pick<GeneratorOptions, 'sourceFileName' | 'filename'>>;
11
+ export declare function generateFromAst(ast: _babel_types.Node, opts?: GenerateFromAstOptions): GeneratorResult;
12
+ export type { GeneratorResult } from '@babel/generator';
13
+ /**
14
+ * Strips TypeScript type-only exports and imports from an AST.
15
+ *
16
+ * This is necessary because babel-dead-code-elimination doesn't handle
17
+ * TypeScript type exports/imports. When a type export references an import
18
+ * that pulls in server-only code, the dead code elimination won't remove
19
+ * that import because it sees the type as still referencing it.
20
+ *
21
+ * This function removes:
22
+ * - `export type Foo = ...`
23
+ * - `export interface Foo { ... }`
24
+ * - `export type { Foo } from './module.js'`
25
+ * - `export type * from './module.js'`
26
+ * - Type specifiers in mixed exports: `export { value, type Foo }` -> `export { value }`
27
+ * - `import type { Foo } from './module.js'`
28
+ * - Type specifiers in mixed imports: `import { value, type Foo } from './module.js'` -> `import { value }`
29
+ *
30
+ * Note: Non-exported type/interface declarations are preserved as they may be
31
+ * used as type annotations within the code.
32
+ *
33
+ * @param ast - The Babel AST (or ParseResult) to mutate
34
+ */
35
+ export declare function stripTypeExports(ast: ParseResult<_babel_types.File>): void;
36
+ export { findReferencedIdentifiers };
37
+ /**
38
+ * Performs dead code elimination on the AST, with TypeScript type stripping.
39
+ *
40
+ * This is a wrapper around babel-dead-code-elimination that first strips
41
+ * TypeScript type-only exports and imports. This is necessary because
42
+ * babel-dead-code-elimination doesn't handle type exports, which can cause
43
+ * imports to be retained when they're only referenced by type exports.
44
+ *
45
+ * @param ast - The Babel AST to mutate
46
+ * @param candidates - Optional set of identifier paths to consider for removal.
47
+ * If provided, only these identifiers will be candidates for removal.
48
+ * This should be the result of `findReferencedIdentifiers(ast)` called
49
+ * before any AST transformations.
50
+ */
51
+ export declare function deadCodeElimination(ast: ParseResult<_babel_types.File>, candidates?: ReturnType<typeof findReferencedIdentifiers>): void;
@@ -0,0 +1,98 @@
1
+ import { parse } from "@babel/parser";
2
+ import _generate from "@babel/generator";
3
+ import * as t from "@babel/types";
4
+ import { deadCodeElimination, findReferencedIdentifiers } from "babel-dead-code-elimination";
5
+ //#region src/ast.ts
6
+ function parseAst({ code, ...opts }) {
7
+ return parse(code, {
8
+ plugins: [
9
+ "jsx",
10
+ "typescript",
11
+ "explicitResourceManagement",
12
+ "decorators"
13
+ ],
14
+ sourceType: "module",
15
+ ...opts
16
+ });
17
+ }
18
+ var generate = _generate;
19
+ if ("default" in generate) generate = generate.default;
20
+ function generateFromAst(ast, opts) {
21
+ return generate(ast, opts ? {
22
+ importAttributesKeyword: "with",
23
+ sourceMaps: true,
24
+ ...opts
25
+ } : void 0);
26
+ }
27
+ /**
28
+ * Strips TypeScript type-only exports and imports from an AST.
29
+ *
30
+ * This is necessary because babel-dead-code-elimination doesn't handle
31
+ * TypeScript type exports/imports. When a type export references an import
32
+ * that pulls in server-only code, the dead code elimination won't remove
33
+ * that import because it sees the type as still referencing it.
34
+ *
35
+ * This function removes:
36
+ * - `export type Foo = ...`
37
+ * - `export interface Foo { ... }`
38
+ * - `export type { Foo } from './module'`
39
+ * - `export type * from './module'`
40
+ * - Type specifiers in mixed exports: `export { value, type Foo }` -> `export { value }`
41
+ * - `import type { Foo } from './module'`
42
+ * - Type specifiers in mixed imports: `import { value, type Foo } from './module'` -> `import { value }`
43
+ *
44
+ * Note: Non-exported type/interface declarations are preserved as they may be
45
+ * used as type annotations within the code.
46
+ *
47
+ * @param ast - The Babel AST (or ParseResult) to mutate
48
+ */
49
+ function stripTypeExports(ast) {
50
+ ast.program.body = ast.program.body.filter((node) => {
51
+ if (t.isExportNamedDeclaration(node)) {
52
+ if (node.exportKind === "type") return false;
53
+ if (node.specifiers.length > 0) {
54
+ node.specifiers = node.specifiers.filter((specifier) => {
55
+ if (t.isExportSpecifier(specifier)) return specifier.exportKind !== "type";
56
+ return true;
57
+ });
58
+ if (node.specifiers.length === 0 && !node.declaration) return false;
59
+ }
60
+ }
61
+ if (t.isExportAllDeclaration(node)) {
62
+ if (node.exportKind === "type") return false;
63
+ }
64
+ if (t.isImportDeclaration(node)) {
65
+ if (node.importKind === "type") return false;
66
+ if (node.specifiers.length > 0) {
67
+ node.specifiers = node.specifiers.filter((specifier) => {
68
+ if (t.isImportSpecifier(specifier)) return specifier.importKind !== "type";
69
+ return true;
70
+ });
71
+ if (node.specifiers.length === 0) return false;
72
+ }
73
+ }
74
+ return true;
75
+ });
76
+ }
77
+ /**
78
+ * Performs dead code elimination on the AST, with TypeScript type stripping.
79
+ *
80
+ * This is a wrapper around babel-dead-code-elimination that first strips
81
+ * TypeScript type-only exports and imports. This is necessary because
82
+ * babel-dead-code-elimination doesn't handle type exports, which can cause
83
+ * imports to be retained when they're only referenced by type exports.
84
+ *
85
+ * @param ast - The Babel AST to mutate
86
+ * @param candidates - Optional set of identifier paths to consider for removal.
87
+ * If provided, only these identifiers will be candidates for removal.
88
+ * This should be the result of `findReferencedIdentifiers(ast)` called
89
+ * before any AST transformations.
90
+ */
91
+ function deadCodeElimination$1(ast, candidates) {
92
+ stripTypeExports(ast);
93
+ deadCodeElimination(ast, candidates);
94
+ }
95
+ //#endregion
96
+ export { deadCodeElimination$1 as deadCodeElimination, findReferencedIdentifiers, generateFromAst, parseAst, stripTypeExports };
97
+
98
+ //# sourceMappingURL=ast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.js","names":[],"sources":["../../src/ast.ts"],"sourcesContent":["import { parse } from '@babel/parser'\nimport _generate from '@babel/generator'\nimport * as t from '@babel/types'\nimport {\n deadCodeElimination as _deadCodeElimination,\n findReferencedIdentifiers,\n} from 'babel-dead-code-elimination'\nimport type { GeneratorOptions, GeneratorResult } from '@babel/generator'\nimport type { ParseResult, ParserOptions } from '@babel/parser'\nimport type * as _babel_types from '@babel/types'\n\nexport type ParseAstOptions = ParserOptions & {\n code: string\n}\n\nexport type ParseAstResult = ParseResult<_babel_types.File>\nexport function parseAst({ code, ...opts }: ParseAstOptions): ParseAstResult {\n return parse(code, {\n plugins: [\n 'jsx',\n 'typescript',\n 'explicitResourceManagement',\n 'decorators', // required for Angular and other decorator-based code\n ],\n sourceType: 'module',\n ...opts,\n })\n}\n\nlet generate = _generate\n\nif ('default' in generate) {\n generate = generate.default as typeof generate\n}\ntype GenerateFromAstOptions = GeneratorOptions &\n Required<Pick<GeneratorOptions, 'sourceFileName' | 'filename'>>\nexport function generateFromAst(\n ast: _babel_types.Node,\n opts?: GenerateFromAstOptions,\n): GeneratorResult {\n return generate(\n ast,\n opts\n ? { importAttributesKeyword: 'with', sourceMaps: true, ...opts }\n : undefined,\n )\n}\nexport type { GeneratorResult } from '@babel/generator'\n\n/**\n * Strips TypeScript type-only exports and imports from an AST.\n *\n * This is necessary because babel-dead-code-elimination doesn't handle\n * TypeScript type exports/imports. When a type export references an import\n * that pulls in server-only code, the dead code elimination won't remove\n * that import because it sees the type as still referencing it.\n *\n * This function removes:\n * - `export type Foo = ...`\n * - `export interface Foo { ... }`\n * - `export type { Foo } from './module'`\n * - `export type * from './module'`\n * - Type specifiers in mixed exports: `export { value, type Foo }` -> `export { value }`\n * - `import type { Foo } from './module'`\n * - Type specifiers in mixed imports: `import { value, type Foo } from './module'` -> `import { value }`\n *\n * Note: Non-exported type/interface declarations are preserved as they may be\n * used as type annotations within the code.\n *\n * @param ast - The Babel AST (or ParseResult) to mutate\n */\nexport function stripTypeExports(ast: ParseResult<_babel_types.File>): void {\n // Filter the program body to remove type-only nodes\n ast.program.body = ast.program.body.filter((node) => {\n // Handle export declarations\n if (t.isExportNamedDeclaration(node)) {\n // Remove entire export if it's a type-only export\n // e.g., `export type Foo = string`, `export interface Bar {}`, `export type { X } from './y'`\n if (node.exportKind === 'type') {\n return false\n }\n\n // For value exports with mixed specifiers, filter out type-only specifiers\n // e.g., `export { value, type TypeOnly }` -> `export { value }`\n if (node.specifiers.length > 0) {\n node.specifiers = node.specifiers.filter((specifier) => {\n if (t.isExportSpecifier(specifier)) {\n return specifier.exportKind !== 'type'\n }\n return true\n })\n\n // If all specifiers were removed, remove the entire export declaration\n // (unless it has a declaration like `export const x = 1`)\n if (node.specifiers.length === 0 && !node.declaration) {\n return false\n }\n }\n }\n\n // Handle type-only export-all declarations\n // e.g., `export type * from './module'`\n if (t.isExportAllDeclaration(node)) {\n if (node.exportKind === 'type') {\n return false\n }\n }\n\n // Handle import declarations\n if (t.isImportDeclaration(node)) {\n // Remove entire import if it's a type-only import\n // e.g., `import type { Foo } from './module'`\n if (node.importKind === 'type') {\n return false\n }\n\n // For value imports with mixed specifiers, filter out type-only specifiers\n // e.g., `import { value, type TypeOnly } from './module'` -> `import { value }`\n if (node.specifiers.length > 0) {\n node.specifiers = node.specifiers.filter((specifier) => {\n if (t.isImportSpecifier(specifier)) {\n return specifier.importKind !== 'type'\n }\n return true\n })\n\n // If all specifiers were removed, remove the entire import declaration\n if (node.specifiers.length === 0) {\n return false\n }\n }\n }\n\n return true\n })\n}\n\n// Re-export findReferencedIdentifiers from babel-dead-code-elimination\nexport { findReferencedIdentifiers }\n\n/**\n * Performs dead code elimination on the AST, with TypeScript type stripping.\n *\n * This is a wrapper around babel-dead-code-elimination that first strips\n * TypeScript type-only exports and imports. This is necessary because\n * babel-dead-code-elimination doesn't handle type exports, which can cause\n * imports to be retained when they're only referenced by type exports.\n *\n * @param ast - The Babel AST to mutate\n * @param candidates - Optional set of identifier paths to consider for removal.\n * If provided, only these identifiers will be candidates for removal.\n * This should be the result of `findReferencedIdentifiers(ast)` called\n * before any AST transformations.\n */\nexport function deadCodeElimination(\n ast: ParseResult<_babel_types.File>,\n candidates?: ReturnType<typeof findReferencedIdentifiers>,\n): void {\n // First strip TypeScript type-only exports and imports\n stripTypeExports(ast)\n\n // Then run the original dead code elimination\n _deadCodeElimination(ast, candidates)\n}\n"],"mappings":";;;;;AAgBA,SAAgB,SAAS,EAAE,MAAM,GAAG,QAAyC;AAC3E,QAAO,MAAM,MAAM;EACjB,SAAS;GACP;GACA;GACA;GACA;GACD;EACD,YAAY;EACZ,GAAG;EACJ,CAAC;;AAGJ,IAAI,WAAW;AAEf,IAAI,aAAa,SACf,YAAW,SAAS;AAItB,SAAgB,gBACd,KACA,MACiB;AACjB,QAAO,SACL,KACA,OACI;EAAE,yBAAyB;EAAQ,YAAY;EAAM,GAAG;EAAM,GAC9D,KAAA,EACL;;;;;;;;;;;;;;;;;;;;;;;;AA0BH,SAAgB,iBAAiB,KAA2C;AAE1E,KAAI,QAAQ,OAAO,IAAI,QAAQ,KAAK,QAAQ,SAAS;AAEnD,MAAI,EAAE,yBAAyB,KAAK,EAAE;AAGpC,OAAI,KAAK,eAAe,OACtB,QAAO;AAKT,OAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,SAAK,aAAa,KAAK,WAAW,QAAQ,cAAc;AACtD,SAAI,EAAE,kBAAkB,UAAU,CAChC,QAAO,UAAU,eAAe;AAElC,YAAO;MACP;AAIF,QAAI,KAAK,WAAW,WAAW,KAAK,CAAC,KAAK,YACxC,QAAO;;;AAOb,MAAI,EAAE,uBAAuB,KAAK;OAC5B,KAAK,eAAe,OACtB,QAAO;;AAKX,MAAI,EAAE,oBAAoB,KAAK,EAAE;AAG/B,OAAI,KAAK,eAAe,OACtB,QAAO;AAKT,OAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,SAAK,aAAa,KAAK,WAAW,QAAQ,cAAc;AACtD,SAAI,EAAE,kBAAkB,UAAU,CAChC,QAAO,UAAU,eAAe;AAElC,YAAO;MACP;AAGF,QAAI,KAAK,WAAW,WAAW,EAC7B,QAAO;;;AAKb,SAAO;GACP;;;;;;;;;;;;;;;;AAoBJ,SAAgB,sBACd,KACA,YACM;AAEN,kBAAiB,IAAI;AAGrB,qBAAqB,KAAK,WAAW"}
@@ -0,0 +1,6 @@
1
+ import { Plugin } from 'vite';
2
+ export declare function copyFilesPlugin({ fromDir, toDir, pattern, }: {
3
+ pattern?: string | Array<string>;
4
+ fromDir: string;
5
+ toDir: string;
6
+ }): Plugin;
@@ -0,0 +1,23 @@
1
+ import { copyFile, mkdir } from "node:fs/promises";
2
+ import { dirname, join } from "pathe";
3
+ import { glob } from "tinyglobby";
4
+ //#region src/copy-files-plugin.ts
5
+ function copyFilesPlugin({ fromDir, toDir, pattern = "**" }) {
6
+ return {
7
+ name: "copy-files",
8
+ async writeBundle() {
9
+ const entries = await glob(pattern, { cwd: fromDir });
10
+ if (entries.length === 0) throw new Error(`No files found matching pattern "${pattern}" in directory "${fromDir}"`);
11
+ for (const entry of entries) {
12
+ const srcPath = join(fromDir, entry);
13
+ const destPath = join(toDir, entry);
14
+ await mkdir(dirname(destPath), { recursive: true });
15
+ await copyFile(srcPath, destPath);
16
+ }
17
+ }
18
+ };
19
+ }
20
+ //#endregion
21
+ export { copyFilesPlugin };
22
+
23
+ //# sourceMappingURL=copy-files-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy-files-plugin.js","names":[],"sources":["../../src/copy-files-plugin.ts"],"sourcesContent":["import { copyFile, mkdir } from 'node:fs/promises'\nimport { dirname, join } from 'pathe'\nimport { glob } from 'tinyglobby'\nimport type { Plugin } from 'vite'\n\nexport function copyFilesPlugin({\n fromDir,\n toDir,\n pattern = '**',\n}: {\n pattern?: string | Array<string>\n fromDir: string\n toDir: string\n}): Plugin {\n return {\n name: 'copy-files',\n async writeBundle() {\n const entries = await glob(pattern, { cwd: fromDir })\n if (entries.length === 0) {\n throw new Error(\n `No files found matching pattern \"${pattern}\" in directory \"${fromDir}\"`,\n )\n }\n\n for (const entry of entries) {\n const srcPath = join(fromDir, entry)\n const destPath = join(toDir, entry)\n // Ensure the destination directory exists\n await mkdir(dirname(destPath), { recursive: true })\n await copyFile(srcPath, destPath)\n }\n },\n }\n}\n"],"mappings":";;;;AAKA,SAAgB,gBAAgB,EAC9B,SACA,OACA,UAAU,QAKD;AACT,QAAO;EACL,MAAM;EACN,MAAM,cAAc;GAClB,MAAM,UAAU,MAAM,KAAK,SAAS,EAAE,KAAK,SAAS,CAAC;AACrD,OAAI,QAAQ,WAAW,EACrB,OAAM,IAAI,MACR,oCAAoC,QAAQ,kBAAkB,QAAQ,GACvE;AAGH,QAAK,MAAM,SAAS,SAAS;IAC3B,MAAM,UAAU,KAAK,SAAS,MAAM;IACpC,MAAM,WAAW,KAAK,OAAO,MAAM;AAEnC,UAAM,MAAM,QAAQ,SAAS,EAAE,EAAE,WAAW,MAAM,CAAC;AACnD,UAAM,SAAS,SAAS,SAAS;;;EAGtC"}
@@ -0,0 +1,4 @@
1
+ export { parseAst, generateFromAst, deadCodeElimination, findReferencedIdentifiers, stripTypeExports, } from './ast.js';
2
+ export type { ParseAstOptions, ParseAstResult, GeneratorResult } from './ast.js';
3
+ export { logDiff } from './logger.js';
4
+ export { copyFilesPlugin } from './copy-files-plugin.js';
@@ -0,0 +1,4 @@
1
+ import { deadCodeElimination, findReferencedIdentifiers, generateFromAst, parseAst, stripTypeExports } from "./ast.js";
2
+ import { logDiff } from "./logger.js";
3
+ import { copyFilesPlugin } from "./copy-files-plugin.js";
4
+ export { copyFilesPlugin, deadCodeElimination, findReferencedIdentifiers, generateFromAst, logDiff, parseAst, stripTypeExports };
@@ -0,0 +1 @@
1
+ export declare function logDiff(oldStr: string, newStr: string): void;
@@ -0,0 +1,48 @@
1
+ import ansis from "ansis";
2
+ import { diffWords } from "diff";
3
+ //#region src/logger.ts
4
+ function logDiff(oldStr, newStr) {
5
+ const differences = diffWords(oldStr, newStr);
6
+ let output = "";
7
+ let unchangedLines = "";
8
+ function processUnchangedLines(lines) {
9
+ const lineArray = lines.split("\n");
10
+ if (lineArray.length > 4) return [
11
+ ansis.dim(lineArray[0]),
12
+ ansis.dim(lineArray[1]),
13
+ "",
14
+ ansis.dim.bold(`... (${lineArray.length - 4} lines) ...`),
15
+ "",
16
+ ansis.dim(lineArray[lineArray.length - 2]),
17
+ ansis.dim(lineArray[lineArray.length - 1])
18
+ ].join("\n");
19
+ return ansis.dim(lines);
20
+ }
21
+ differences.forEach((part, index) => {
22
+ const nextPart = differences[index + 1];
23
+ if (part.added) {
24
+ if (unchangedLines) {
25
+ output += processUnchangedLines(unchangedLines);
26
+ unchangedLines = "";
27
+ }
28
+ output += ansis.green.bold(part.value);
29
+ if (nextPart?.removed) output += " ";
30
+ } else if (part.removed) {
31
+ if (unchangedLines) {
32
+ output += processUnchangedLines(unchangedLines);
33
+ unchangedLines = "";
34
+ }
35
+ output += ansis.red.bold(part.value);
36
+ if (nextPart?.added) output += " ";
37
+ } else unchangedLines += part.value;
38
+ });
39
+ if (unchangedLines) output += processUnchangedLines(unchangedLines);
40
+ if (output) {
41
+ console.log("\nDiff:");
42
+ console.log(output + "\n\n");
43
+ } else console.log("No changes");
44
+ }
45
+ //#endregion
46
+ export { logDiff };
47
+
48
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","names":[],"sources":["../../src/logger.ts"],"sourcesContent":["import ansis from 'ansis'\nimport { diffWords } from 'diff'\n\nexport function logDiff(oldStr: string, newStr: string) {\n const differences = diffWords(oldStr, newStr)\n\n let output = ''\n let unchangedLines = ''\n\n function processUnchangedLines(lines: string): string {\n const lineArray = lines.split('\\n')\n if (lineArray.length > 4) {\n return [\n ansis.dim(lineArray[0]),\n ansis.dim(lineArray[1]),\n '',\n ansis.dim.bold(`... (${lineArray.length - 4} lines) ...`),\n '',\n ansis.dim(lineArray[lineArray.length - 2]),\n ansis.dim(lineArray[lineArray.length - 1]),\n ].join('\\n')\n }\n return ansis.dim(lines)\n }\n\n differences.forEach((part, index) => {\n const nextPart = differences[index + 1]\n\n if (part.added) {\n if (unchangedLines) {\n output += processUnchangedLines(unchangedLines)\n unchangedLines = ''\n }\n output += ansis.green.bold(part.value)\n if (nextPart?.removed) output += ' '\n } else if (part.removed) {\n if (unchangedLines) {\n output += processUnchangedLines(unchangedLines)\n unchangedLines = ''\n }\n output += ansis.red.bold(part.value)\n if (nextPart?.added) output += ' '\n } else {\n unchangedLines += part.value\n }\n })\n\n // Process any remaining unchanged lines at the end\n if (unchangedLines) {\n output += processUnchangedLines(unchangedLines)\n }\n\n if (output) {\n console.log('\\nDiff:')\n console.log(output + '\\n\\n')\n } else {\n console.log('No changes')\n }\n}\n"],"mappings":";;;AAGA,SAAgB,QAAQ,QAAgB,QAAgB;CACtD,MAAM,cAAc,UAAU,QAAQ,OAAO;CAE7C,IAAI,SAAS;CACb,IAAI,iBAAiB;CAErB,SAAS,sBAAsB,OAAuB;EACpD,MAAM,YAAY,MAAM,MAAM,KAAK;AACnC,MAAI,UAAU,SAAS,EACrB,QAAO;GACL,MAAM,IAAI,UAAU,GAAG;GACvB,MAAM,IAAI,UAAU,GAAG;GACvB;GACA,MAAM,IAAI,KAAK,QAAQ,UAAU,SAAS,EAAE,aAAa;GACzD;GACA,MAAM,IAAI,UAAU,UAAU,SAAS,GAAG;GAC1C,MAAM,IAAI,UAAU,UAAU,SAAS,GAAG;GAC3C,CAAC,KAAK,KAAK;AAEd,SAAO,MAAM,IAAI,MAAM;;AAGzB,aAAY,SAAS,MAAM,UAAU;EACnC,MAAM,WAAW,YAAY,QAAQ;AAErC,MAAI,KAAK,OAAO;AACd,OAAI,gBAAgB;AAClB,cAAU,sBAAsB,eAAe;AAC/C,qBAAiB;;AAEnB,aAAU,MAAM,MAAM,KAAK,KAAK,MAAM;AACtC,OAAI,UAAU,QAAS,WAAU;aACxB,KAAK,SAAS;AACvB,OAAI,gBAAgB;AAClB,cAAU,sBAAsB,eAAe;AAC/C,qBAAiB;;AAEnB,aAAU,MAAM,IAAI,KAAK,KAAK,MAAM;AACpC,OAAI,UAAU,MAAO,WAAU;QAE/B,mBAAkB,KAAK;GAEzB;AAGF,KAAI,eACF,WAAU,sBAAsB,eAAe;AAGjD,KAAI,QAAQ;AACV,UAAQ,IAAI,UAAU;AACtB,UAAQ,IAAI,SAAS,OAAO;OAE5B,SAAQ,IAAI,aAAa"}
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "@benjavicente/router-utils",
3
+ "version": "1.161.6",
4
+ "description": "Modern and scalable routing for React applications",
5
+ "author": "Tanner Linsley",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/TanStack/router.git",
10
+ "directory": "packages/router-utils"
11
+ },
12
+ "homepage": "https://tanstack.com/router",
13
+ "funding": {
14
+ "type": "github",
15
+ "url": "https://github.com/sponsors/tannerlinsley"
16
+ },
17
+ "keywords": [
18
+ "react",
19
+ "location",
20
+ "router",
21
+ "routing",
22
+ "async",
23
+ "async router",
24
+ "typescript"
25
+ ],
26
+ "type": "module",
27
+ "types": "dist/esm/index.d.ts",
28
+ "main": "dist/cjs/index.cjs",
29
+ "module": "dist/esm/index.js",
30
+ "exports": {
31
+ ".": {
32
+ "import": {
33
+ "types": "./dist/esm/index.d.ts",
34
+ "default": "./dist/esm/index.js"
35
+ },
36
+ "require": {
37
+ "types": "./dist/cjs/index.d.cts",
38
+ "default": "./dist/cjs/index.cjs"
39
+ }
40
+ },
41
+ "./package.json": "./package.json"
42
+ },
43
+ "sideEffects": false,
44
+ "files": [
45
+ "dist",
46
+ "src"
47
+ ],
48
+ "engines": {
49
+ "node": ">=20.19"
50
+ },
51
+ "dependencies": {
52
+ "@babel/core": "^7.28.5",
53
+ "@babel/generator": "^7.28.5",
54
+ "@babel/parser": "^7.28.5",
55
+ "@babel/types": "^7.28.5",
56
+ "ansis": "^4.1.0",
57
+ "babel-dead-code-elimination": "^1.0.12",
58
+ "diff": "^8.0.2",
59
+ "pathe": "^2.0.3",
60
+ "tinyglobby": "^0.2.15"
61
+ },
62
+ "devDependencies": {
63
+ "@types/babel__core": "^7.20.5",
64
+ "@types/babel__generator": "^7.27.0",
65
+ "@types/diff": "^7.0.2",
66
+ "vite": "*",
67
+ "@types/node": ">=20"
68
+ },
69
+ "scripts": {
70
+ "clean": "rimraf ./dist && rimraf ./coverage",
71
+ "test:eslint": "eslint ./src",
72
+ "test:types": "pnpm run \"/^test:types:ts[0-9]{2}$/\"",
73
+ "test:types:ts55": "node ../../node_modules/typescript55/lib/tsc.js",
74
+ "test:types:ts56": "node ../../node_modules/typescript56/lib/tsc.js",
75
+ "test:types:ts57": "node ../../node_modules/typescript57/lib/tsc.js",
76
+ "test:types:ts58": "node ../../node_modules/typescript58/lib/tsc.js",
77
+ "test:types:ts59": "node ../../node_modules/typescript59/lib/tsc.js",
78
+ "test:types:ts60": "tsc",
79
+ "test:unit": "exit 0; vitest --typecheck",
80
+ "test:build": "publint --strict && attw --ignore-rules no-resolution --pack .",
81
+ "build": "vite build"
82
+ }
83
+ }
package/src/ast.ts ADDED
@@ -0,0 +1,164 @@
1
+ import { parse } from '@babel/parser'
2
+ import _generate from '@babel/generator'
3
+ import * as t from '@babel/types'
4
+ import {
5
+ deadCodeElimination as _deadCodeElimination,
6
+ findReferencedIdentifiers,
7
+ } from 'babel-dead-code-elimination'
8
+ import type { GeneratorOptions, GeneratorResult } from '@babel/generator'
9
+ import type { ParseResult, ParserOptions } from '@babel/parser'
10
+ import type * as _babel_types from '@babel/types'
11
+
12
+ export type ParseAstOptions = ParserOptions & {
13
+ code: string
14
+ }
15
+
16
+ export type ParseAstResult = ParseResult<_babel_types.File>
17
+ export function parseAst({ code, ...opts }: ParseAstOptions): ParseAstResult {
18
+ return parse(code, {
19
+ plugins: [
20
+ 'jsx',
21
+ 'typescript',
22
+ 'explicitResourceManagement',
23
+ 'decorators', // required for Angular and other decorator-based code
24
+ ],
25
+ sourceType: 'module',
26
+ ...opts,
27
+ })
28
+ }
29
+
30
+ let generate = _generate
31
+
32
+ if ('default' in generate) {
33
+ generate = generate.default as typeof generate
34
+ }
35
+ type GenerateFromAstOptions = GeneratorOptions &
36
+ Required<Pick<GeneratorOptions, 'sourceFileName' | 'filename'>>
37
+ export function generateFromAst(
38
+ ast: _babel_types.Node,
39
+ opts?: GenerateFromAstOptions,
40
+ ): GeneratorResult {
41
+ return generate(
42
+ ast,
43
+ opts
44
+ ? { importAttributesKeyword: 'with', sourceMaps: true, ...opts }
45
+ : undefined,
46
+ )
47
+ }
48
+ export type { GeneratorResult } from '@babel/generator'
49
+
50
+ /**
51
+ * Strips TypeScript type-only exports and imports from an AST.
52
+ *
53
+ * This is necessary because babel-dead-code-elimination doesn't handle
54
+ * TypeScript type exports/imports. When a type export references an import
55
+ * that pulls in server-only code, the dead code elimination won't remove
56
+ * that import because it sees the type as still referencing it.
57
+ *
58
+ * This function removes:
59
+ * - `export type Foo = ...`
60
+ * - `export interface Foo { ... }`
61
+ * - `export type { Foo } from './module'`
62
+ * - `export type * from './module'`
63
+ * - Type specifiers in mixed exports: `export { value, type Foo }` -> `export { value }`
64
+ * - `import type { Foo } from './module'`
65
+ * - Type specifiers in mixed imports: `import { value, type Foo } from './module'` -> `import { value }`
66
+ *
67
+ * Note: Non-exported type/interface declarations are preserved as they may be
68
+ * used as type annotations within the code.
69
+ *
70
+ * @param ast - The Babel AST (or ParseResult) to mutate
71
+ */
72
+ export function stripTypeExports(ast: ParseResult<_babel_types.File>): void {
73
+ // Filter the program body to remove type-only nodes
74
+ ast.program.body = ast.program.body.filter((node) => {
75
+ // Handle export declarations
76
+ if (t.isExportNamedDeclaration(node)) {
77
+ // Remove entire export if it's a type-only export
78
+ // e.g., `export type Foo = string`, `export interface Bar {}`, `export type { X } from './y'`
79
+ if (node.exportKind === 'type') {
80
+ return false
81
+ }
82
+
83
+ // For value exports with mixed specifiers, filter out type-only specifiers
84
+ // e.g., `export { value, type TypeOnly }` -> `export { value }`
85
+ if (node.specifiers.length > 0) {
86
+ node.specifiers = node.specifiers.filter((specifier) => {
87
+ if (t.isExportSpecifier(specifier)) {
88
+ return specifier.exportKind !== 'type'
89
+ }
90
+ return true
91
+ })
92
+
93
+ // If all specifiers were removed, remove the entire export declaration
94
+ // (unless it has a declaration like `export const x = 1`)
95
+ if (node.specifiers.length === 0 && !node.declaration) {
96
+ return false
97
+ }
98
+ }
99
+ }
100
+
101
+ // Handle type-only export-all declarations
102
+ // e.g., `export type * from './module'`
103
+ if (t.isExportAllDeclaration(node)) {
104
+ if (node.exportKind === 'type') {
105
+ return false
106
+ }
107
+ }
108
+
109
+ // Handle import declarations
110
+ if (t.isImportDeclaration(node)) {
111
+ // Remove entire import if it's a type-only import
112
+ // e.g., `import type { Foo } from './module'`
113
+ if (node.importKind === 'type') {
114
+ return false
115
+ }
116
+
117
+ // For value imports with mixed specifiers, filter out type-only specifiers
118
+ // e.g., `import { value, type TypeOnly } from './module'` -> `import { value }`
119
+ if (node.specifiers.length > 0) {
120
+ node.specifiers = node.specifiers.filter((specifier) => {
121
+ if (t.isImportSpecifier(specifier)) {
122
+ return specifier.importKind !== 'type'
123
+ }
124
+ return true
125
+ })
126
+
127
+ // If all specifiers were removed, remove the entire import declaration
128
+ if (node.specifiers.length === 0) {
129
+ return false
130
+ }
131
+ }
132
+ }
133
+
134
+ return true
135
+ })
136
+ }
137
+
138
+ // Re-export findReferencedIdentifiers from babel-dead-code-elimination
139
+ export { findReferencedIdentifiers }
140
+
141
+ /**
142
+ * Performs dead code elimination on the AST, with TypeScript type stripping.
143
+ *
144
+ * This is a wrapper around babel-dead-code-elimination that first strips
145
+ * TypeScript type-only exports and imports. This is necessary because
146
+ * babel-dead-code-elimination doesn't handle type exports, which can cause
147
+ * imports to be retained when they're only referenced by type exports.
148
+ *
149
+ * @param ast - The Babel AST to mutate
150
+ * @param candidates - Optional set of identifier paths to consider for removal.
151
+ * If provided, only these identifiers will be candidates for removal.
152
+ * This should be the result of `findReferencedIdentifiers(ast)` called
153
+ * before any AST transformations.
154
+ */
155
+ export function deadCodeElimination(
156
+ ast: ParseResult<_babel_types.File>,
157
+ candidates?: ReturnType<typeof findReferencedIdentifiers>,
158
+ ): void {
159
+ // First strip TypeScript type-only exports and imports
160
+ stripTypeExports(ast)
161
+
162
+ // Then run the original dead code elimination
163
+ _deadCodeElimination(ast, candidates)
164
+ }
@@ -0,0 +1,34 @@
1
+ import { copyFile, mkdir } from 'node:fs/promises'
2
+ import { dirname, join } from 'pathe'
3
+ import { glob } from 'tinyglobby'
4
+ import type { Plugin } from 'vite'
5
+
6
+ export function copyFilesPlugin({
7
+ fromDir,
8
+ toDir,
9
+ pattern = '**',
10
+ }: {
11
+ pattern?: string | Array<string>
12
+ fromDir: string
13
+ toDir: string
14
+ }): Plugin {
15
+ return {
16
+ name: 'copy-files',
17
+ async writeBundle() {
18
+ const entries = await glob(pattern, { cwd: fromDir })
19
+ if (entries.length === 0) {
20
+ throw new Error(
21
+ `No files found matching pattern "${pattern}" in directory "${fromDir}"`,
22
+ )
23
+ }
24
+
25
+ for (const entry of entries) {
26
+ const srcPath = join(fromDir, entry)
27
+ const destPath = join(toDir, entry)
28
+ // Ensure the destination directory exists
29
+ await mkdir(dirname(destPath), { recursive: true })
30
+ await copyFile(srcPath, destPath)
31
+ }
32
+ },
33
+ }
34
+ }
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ export {
2
+ parseAst,
3
+ generateFromAst,
4
+ deadCodeElimination,
5
+ findReferencedIdentifiers,
6
+ stripTypeExports,
7
+ } from './ast'
8
+ export type { ParseAstOptions, ParseAstResult, GeneratorResult } from './ast'
9
+ export { logDiff } from './logger'
10
+
11
+ export { copyFilesPlugin } from './copy-files-plugin'
package/src/logger.ts ADDED
@@ -0,0 +1,59 @@
1
+ import ansis from 'ansis'
2
+ import { diffWords } from 'diff'
3
+
4
+ export function logDiff(oldStr: string, newStr: string) {
5
+ const differences = diffWords(oldStr, newStr)
6
+
7
+ let output = ''
8
+ let unchangedLines = ''
9
+
10
+ function processUnchangedLines(lines: string): string {
11
+ const lineArray = lines.split('\n')
12
+ if (lineArray.length > 4) {
13
+ return [
14
+ ansis.dim(lineArray[0]),
15
+ ansis.dim(lineArray[1]),
16
+ '',
17
+ ansis.dim.bold(`... (${lineArray.length - 4} lines) ...`),
18
+ '',
19
+ ansis.dim(lineArray[lineArray.length - 2]),
20
+ ansis.dim(lineArray[lineArray.length - 1]),
21
+ ].join('\n')
22
+ }
23
+ return ansis.dim(lines)
24
+ }
25
+
26
+ differences.forEach((part, index) => {
27
+ const nextPart = differences[index + 1]
28
+
29
+ if (part.added) {
30
+ if (unchangedLines) {
31
+ output += processUnchangedLines(unchangedLines)
32
+ unchangedLines = ''
33
+ }
34
+ output += ansis.green.bold(part.value)
35
+ if (nextPart?.removed) output += ' '
36
+ } else if (part.removed) {
37
+ if (unchangedLines) {
38
+ output += processUnchangedLines(unchangedLines)
39
+ unchangedLines = ''
40
+ }
41
+ output += ansis.red.bold(part.value)
42
+ if (nextPart?.added) output += ' '
43
+ } else {
44
+ unchangedLines += part.value
45
+ }
46
+ })
47
+
48
+ // Process any remaining unchanged lines at the end
49
+ if (unchangedLines) {
50
+ output += processUnchangedLines(unchangedLines)
51
+ }
52
+
53
+ if (output) {
54
+ console.log('\nDiff:')
55
+ console.log(output + '\n\n')
56
+ } else {
57
+ console.log('No changes')
58
+ }
59
+ }