unplugin-keywords 2.6.2 → 2.7.1
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/README.md +35 -41
- package/dist/api.js +1 -1
- package/dist/esbuild.js +1 -1
- package/dist/{hash-DVFA0jSt.js → hash-CRJe3IJz.js} +24 -10
- package/dist/hash-CRJe3IJz.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/{plugin-cgQoj2wo.js → plugin-DZh87VhX.js} +2 -2
- package/dist/{plugin-cgQoj2wo.js.map → plugin-DZh87VhX.js.map} +1 -1
- package/dist/rollup.js +1 -1
- package/dist/vite.js +1 -1
- package/dist/webpack.js +1 -1
- package/package.json +1 -1
- package/dist/hash-DVFA0jSt.js.map +0 -1
package/README.md
CHANGED
|
@@ -19,32 +19,32 @@ A build plugin for structural string literal minification and obfuscation.
|
|
|
19
19
|
|
|
20
20
|
Traditional JavaScript minifiers rely on property mangling (e.g., Terser's `mangle.properties`) to reduce structural identifiers. `unplugin-keywords` provides a module-based alternative that addresses the structural limitations of global mangling.
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
- **Explicit Opt-In:**
|
|
23
|
+
Traditional property mangling requires maintaining complex, global exclusion rules (e.g., [`mangle.json`](https://github.com/preactjs/signals/blob/main/mangle.json)), which are fragile and hard to scale. `unplugin-keywords` utilizes explicit imports (`import * as K from '~keywords'`). Developers unambiguously declare which identifiers are safe to obfuscate directly in the source code.
|
|
24
|
+
- **Gradual Adoption:**
|
|
25
|
+
Unlike global mangling flags that affect the entire codebase simultaneously, installing this plugin alters nothing by default. It allows incremental adoption on a per-file or per-module basis.
|
|
26
|
+
- **Cross-Boundary Consistency:**
|
|
27
|
+
Standard mangled properties cannot safely cross package boundaries; a property mangled to `a` in Package A will not map to `a` in Package B. Because `~keywords` relies on deterministic hashing, identical keys inherently produce identical hashes across independent builds, preserving structural contracts.
|
|
28
|
+
- **Universal Application:**
|
|
29
|
+
Standard minifiers only mangle object keys, leaving string literal values intact. This plugin processes both keys and values uniformly (e.g., `[K.type]: K.SET_USER`). It extends obfuscation to literal types (`const mode: typeof K.extract | typeof K.transform = K.extract`) and even arbitrary static strings (`throw new Error(K['Invalid State'])`).
|
|
30
|
+
- **Trade-offs:**
|
|
31
|
+
This explicit approach sacrifices some source code readability. Furthermore, as demonstrated in the benchmarks below, standard gzip compression handles unmodified semantic strings highly effectively. If reducing the gzipped network payload is the sole objective, the effort of adopting this plugin may not justify the minimal payload reduction.
|
|
32
32
|
|
|
33
33
|
## Visual Demo: `@preact/signals-core`
|
|
34
34
|
|
|
35
35
|
A side-by-side comparison of minified bundles:
|
|
36
36
|
|
|
37
|
-
|
|
|
38
|
-
|
|
37
|
+
| [Unmodified](https://github.com/cueaz/unplugin-keywords/blob/main/demo/signals/src/original.ts) (Standard Minification) | [Keywordified](https://github.com/cueaz/unplugin-keywords/blob/main/demo/signals/src/keywordified.ts) (Literal Obfuscation) |
|
|
38
|
+
| :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
|
|
39
39
|
| <picture><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/cueaz/unplugin-keywords/refs/heads/main/demo/signals/dist_sample/original.min.js.light.png" width="400"><img src="https://raw.githubusercontent.com/cueaz/unplugin-keywords/refs/heads/main/demo/signals/dist_sample/original.min.js.dark.png" width="400" alt="Original"></picture> | <picture><source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/cueaz/unplugin-keywords/refs/heads/main/demo/signals/dist_sample/keywordified.min.js.light.png" width="400"><img src="https://raw.githubusercontent.com/cueaz/unplugin-keywords/refs/heads/main/demo/signals/dist_sample/keywordified.min.js.dark.png" width="400" alt="Keywordified"></picture> |
|
|
40
|
-
|
|
|
40
|
+
| 6.86 kB │ gzip: **2.09 kB** | **5.40 kB** │ gzip: 2.05 kB |
|
|
41
41
|
|
|
42
42
|
> [!NOTE]
|
|
43
43
|
> **Baseline Metrics:** Both the "Unmodified" and "Keywordified" metrics represent standard `tsdown` minification. The official [`@preact/signals-core@1.14.1`](https://bundlephobia.com/package/@preact/signals-core@1.14.1) release achieves a smaller footprint (5.4 kB Minified / 1.9 kB Gzipped) by employing a hand-crafted [`mangle.json`](https://github.com/preactjs/signals/blob/main/mangle.json) for manual property obfuscation.
|
|
44
44
|
>
|
|
45
45
|
> **Compression Efficiency:** While the uncompressed bundle size is reduced by 21.3%, the gzipped size is only 1.9% smaller. This demonstrates the effectiveness of standard gzip compression on unmodified code: if minimizing the gzipped network payload is the sole objective, adopting this plugin is unnecessary.
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
_For more information, see the [demo documentation](https://github.com/cueaz/unplugin-keywords/blob/main/demo/signals/README.md)._
|
|
48
48
|
|
|
49
49
|
## How It Works
|
|
50
50
|
|
|
@@ -56,7 +56,7 @@ Standard minifiers operate exclusively on variable bindings and function names,
|
|
|
56
56
|
Developers reference strings via a virtual module. The strongly recommended pattern is to use a namespace import (`import * as K`), which clearly demarcates keyword usage throughout the file.
|
|
57
57
|
|
|
58
58
|
```ts
|
|
59
|
-
import * as K from '
|
|
59
|
+
import * as K from '~keywords';
|
|
60
60
|
|
|
61
61
|
const action = {
|
|
62
62
|
[K.type]: K.SET_USER,
|
|
@@ -70,22 +70,24 @@ During the build phase, the plugin traverses the AST, resolving bindings and sta
|
|
|
70
70
|
**3. Minified Output (Production):**
|
|
71
71
|
The bundler receives the transformed code and processes the hashed literals. Depending on the frequency of usage, the minifier will either inline the strings directly or extract them into single-character variables to save bytes.
|
|
72
72
|
|
|
73
|
+
<!-- prettier-ignore-start -->
|
|
73
74
|
```ts
|
|
74
75
|
// Example of minifier output: strings may be inlined or assigned to variables if used multiple times
|
|
75
76
|
const _="z2pL21k";const a={a3fB9zX:_,k1Mw8pA:data};
|
|
76
77
|
```
|
|
78
|
+
<!-- prettier-ignore-end -->
|
|
77
79
|
|
|
78
80
|
## Dual-Module Architecture
|
|
79
81
|
|
|
80
82
|
`unplugin-keywords` provides two distinct virtual modules. While exclusively using `K.*` is a perfectly valid and robust approach, the dual-module system allows further bundle size reduction.
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
- **`~keywords` (Stable Hash):**
|
|
85
|
+
Generates deterministic, key-derived hashes (e.g., `"z2pL21k"`). Designed for **public-facing APIs** and structural contracts that must remain consistent across package boundaries (e.g., `package.json` exports).
|
|
86
|
+
_Convention:_ `import * as K from '~keywords';`
|
|
85
87
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
- **`~keywords/local` (Lexical Counter):**
|
|
89
|
+
Generates the shortest possible sequential identifiers (min length: 2, e.g., `"a0"`, `"b0"`). Intended for **internal and local** implementations where cross-boundary stability is irrelevant.
|
|
90
|
+
_Convention:_ `import * as L from '~keywords/local';`
|
|
89
91
|
|
|
90
92
|
**Module Separation:**
|
|
91
93
|
To minimize bundle size, identifiers can be partitioned: bind public interfaces to `K.*`, and obscure all internal state and private members behind `L.*`.
|
|
@@ -116,23 +118,12 @@ export default defineConfig(({ mode }) => ({
|
|
|
116
118
|
}));
|
|
117
119
|
```
|
|
118
120
|
|
|
119
|
-
To enable type checking and IDE auto-completion, execute the CLI
|
|
121
|
+
To enable type checking and IDE auto-completion, execute the CLI. It will automatically generate type declarations and a `package.json` inside `node_modules/~keywords`, allowing your project to resolve the virtual modules:
|
|
120
122
|
|
|
121
123
|
```bash
|
|
122
124
|
npx keywords
|
|
123
125
|
```
|
|
124
126
|
|
|
125
|
-
```jsonc
|
|
126
|
-
{
|
|
127
|
-
"compilerOptions": {
|
|
128
|
-
"paths": {
|
|
129
|
-
"virtual:keywords": ["./node_modules/.keywords/index.d.ts"],
|
|
130
|
-
"virtual:keywords/local": ["./node_modules/.keywords/local.d.ts"]
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
```
|
|
135
|
-
|
|
136
127
|
> [!TIP]
|
|
137
128
|
> During development, the plugin automatically runs a background type generation process while the bundler is running. Manual CLI execution is only necessary for pre-flight type checking (e.g., in CI) before the bundler runs.
|
|
138
129
|
|
|
@@ -151,8 +142,8 @@ The namespace import pattern is applicable in class-based architectures where st
|
|
|
151
142
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
152
143
|
*/
|
|
153
144
|
|
|
154
|
-
import * as K from '
|
|
155
|
-
import * as L from '
|
|
145
|
+
import * as K from '~keywords';
|
|
146
|
+
import * as L from '~keywords/local';
|
|
156
147
|
|
|
157
148
|
import {
|
|
158
149
|
AsyncDirective,
|
|
@@ -172,7 +163,10 @@ export class AsyncReplaceDirective extends AsyncDirective {
|
|
|
172
163
|
return noChange;
|
|
173
164
|
}
|
|
174
165
|
|
|
175
|
-
override [K.update](
|
|
166
|
+
override [K.update](
|
|
167
|
+
_part: ChildPart,
|
|
168
|
+
[value, mapper]: DirectiveParameters<this>,
|
|
169
|
+
) {
|
|
176
170
|
if (!this[K.isConnected]) {
|
|
177
171
|
this[K.disconnected]();
|
|
178
172
|
}
|
|
@@ -221,17 +215,17 @@ export class AsyncReplaceDirective extends AsyncDirective {
|
|
|
221
215
|
}
|
|
222
216
|
}
|
|
223
217
|
```
|
|
224
|
-
|
|
218
|
+
|
|
219
|
+
_In production, all internal properties (e.g., `__value`, `__pauser`) will be completely minified to short sequence identifiers (via `~keywords/local`), obfuscating internal property names from the bundled Lit component._
|
|
225
220
|
|
|
226
221
|
> [!TIP]
|
|
227
222
|
> Native ECMAScript private fields (`#prop`) are safely mangled by standard minifiers, eliminating the need for plugin obfuscation for internal class state.
|
|
228
223
|
|
|
229
|
-
|
|
230
224
|
## Other Supported Patterns
|
|
231
225
|
|
|
232
226
|
```ts
|
|
233
227
|
// Modular Imports
|
|
234
|
-
import { type, 'kebab-case' as kebab } from '
|
|
228
|
+
import { type, 'kebab-case' as kebab } from '~keywords';
|
|
235
229
|
|
|
236
230
|
// JSX Injection
|
|
237
231
|
const View = () => (
|
|
@@ -247,10 +241,10 @@ interface StateMachine {
|
|
|
247
241
|
}
|
|
248
242
|
|
|
249
243
|
// Module Re-exports
|
|
250
|
-
export { internalState as state } from '
|
|
244
|
+
export { internalState as state } from '~keywords';
|
|
251
245
|
|
|
252
246
|
// UNSUPPORTED: Export All (Lacks static traceability)
|
|
253
|
-
export * from '
|
|
247
|
+
export * from '~keywords';
|
|
254
248
|
```
|
|
255
249
|
|
|
256
250
|
## License
|
package/dist/api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as transformCode, i as extractKeywords, n as createHasher, r as createRunner, t as createCounter } from "./hash-
|
|
1
|
+
import { a as transformCode, i as extractKeywords, n as createHasher, r as createRunner, t as createCounter } from "./hash-CRJe3IJz.js";
|
|
2
2
|
//#region src/api.ts
|
|
3
3
|
/**
|
|
4
4
|
* @license
|
package/dist/esbuild.js
CHANGED
|
@@ -9,8 +9,8 @@ import { createHmac, hkdfSync } from "node:crypto";
|
|
|
9
9
|
* @license
|
|
10
10
|
* SPDX-License-Identifier: MIT
|
|
11
11
|
*/
|
|
12
|
-
const VIRTUAL_MODULE_ID = "
|
|
13
|
-
const VIRTUAL_LOCAL_MODULE_ID = "
|
|
12
|
+
const VIRTUAL_MODULE_ID = "~keywords";
|
|
13
|
+
const VIRTUAL_LOCAL_MODULE_ID = "~keywords/local";
|
|
14
14
|
const PLUGIN_NAME = "unplugin-keywords";
|
|
15
15
|
//#endregion
|
|
16
16
|
//#region src/internal/encode.ts
|
|
@@ -94,7 +94,7 @@ const transformPlugin = (mode) => {
|
|
|
94
94
|
},
|
|
95
95
|
ImportDeclaration(path, state) {
|
|
96
96
|
const sourceValue = path.node.source.value;
|
|
97
|
-
if (sourceValue !== "
|
|
97
|
+
if (sourceValue !== "~keywords" && sourceValue !== "~keywords/local") return;
|
|
98
98
|
const isLocal = sourceValue === VIRTUAL_LOCAL_MODULE_ID;
|
|
99
99
|
const targetSet = isLocal ? state.keywords.local : state.keywords.main;
|
|
100
100
|
const targetMap = isLocal ? state.keywordUids.local : state.keywordUids.main;
|
|
@@ -177,8 +177,8 @@ const transformPlugin = (mode) => {
|
|
|
177
177
|
},
|
|
178
178
|
ExportNamedDeclaration(path, state) {
|
|
179
179
|
const sourceValue = path.node.source?.value;
|
|
180
|
-
if (sourceValue !== "
|
|
181
|
-
const targetSet = sourceValue === "
|
|
180
|
+
if (sourceValue !== "~keywords" && sourceValue !== "~keywords/local") return;
|
|
181
|
+
const targetSet = sourceValue === "~keywords/local" ? state.keywords.local : state.keywords.main;
|
|
182
182
|
if (mode === "extract") {
|
|
183
183
|
for (const specifierPath of path.get("specifiers")) if (specifierPath.isExportSpecifier()) {
|
|
184
184
|
const local = specifierPath.node.local;
|
|
@@ -204,7 +204,7 @@ const transformPlugin = (mode) => {
|
|
|
204
204
|
};
|
|
205
205
|
};
|
|
206
206
|
const transformCode = (code, id) => {
|
|
207
|
-
if (!code.includes("
|
|
207
|
+
if (!code.includes("~keywords") && !code.includes("~keywords/local")) return null;
|
|
208
208
|
const result = transformSync(code, {
|
|
209
209
|
babelrc: false,
|
|
210
210
|
configFile: false,
|
|
@@ -227,7 +227,7 @@ const transformCode = (code, id) => {
|
|
|
227
227
|
};
|
|
228
228
|
};
|
|
229
229
|
const extractKeywords = (code) => {
|
|
230
|
-
if (!code.includes("
|
|
230
|
+
if (!code.includes("~keywords") && !code.includes("~keywords/local")) return null;
|
|
231
231
|
let result;
|
|
232
232
|
try {
|
|
233
233
|
result = transformSync(code, {
|
|
@@ -311,8 +311,21 @@ const collectKeywordsFromRoot = async (root, silent, ignoredDirs = [], concurren
|
|
|
311
311
|
if (!silent) console.error(`Scan complete: ${processed}/${files.length} files, ${collectedKeywords.main.size} main, ${collectedKeywords.local.size} local keywords (${elapsed.toFixed(2)}ms).`);
|
|
312
312
|
return collectedKeywords;
|
|
313
313
|
};
|
|
314
|
+
const pkgJson = {
|
|
315
|
+
name: "~keywords",
|
|
316
|
+
version: "0.0.0",
|
|
317
|
+
private: true,
|
|
318
|
+
type: "module",
|
|
319
|
+
sideEffects: false,
|
|
320
|
+
exports: {
|
|
321
|
+
".": { types: "./index.d.ts" },
|
|
322
|
+
"./local": { types: "./local.d.ts" },
|
|
323
|
+
[`./_/*`]: `./_/*`,
|
|
324
|
+
[`./local/_/*`]: `./local/_/*`
|
|
325
|
+
}
|
|
326
|
+
};
|
|
314
327
|
const createRunner = (options) => {
|
|
315
|
-
const { root = process.cwd(), silent = false, outDir = path.join("node_modules", "
|
|
328
|
+
const { root = process.cwd(), silent = false, outDir = path.join("node_modules", "~keywords") } = options ?? {};
|
|
316
329
|
return {
|
|
317
330
|
async collect() {
|
|
318
331
|
return collectKeywordsFromRoot(root, silent);
|
|
@@ -324,6 +337,7 @@ const createRunner = (options) => {
|
|
|
324
337
|
await mkdir(outPath, { recursive: true });
|
|
325
338
|
await writeFile(path.join(outPath, "index.d.ts"), `${content.trim()}\n`);
|
|
326
339
|
await writeFile(path.join(outPath, "local.d.ts"), `${localContent.trim()}\n`);
|
|
340
|
+
await writeFile(path.join(outPath, "package.json"), `${JSON.stringify(pkgJson, null, 2)}\n`);
|
|
327
341
|
},
|
|
328
342
|
async run() {
|
|
329
343
|
const keywords = await this.collect();
|
|
@@ -349,7 +363,7 @@ const createHasher = (secret) => {
|
|
|
349
363
|
const cache = /* @__PURE__ */ new Map();
|
|
350
364
|
return (input) => {
|
|
351
365
|
if (cache.has(input)) return cache.get(input);
|
|
352
|
-
const payload = `
|
|
366
|
+
const payload = `9:${VIRTUAL_MODULE_ID}|${input.length}:${input}`;
|
|
353
367
|
const buffer = createHmac("sha256", secret).update(payload).digest("hex");
|
|
354
368
|
let entropy = BigInt(`0x${buffer}`);
|
|
355
369
|
let result = "";
|
|
@@ -407,4 +421,4 @@ const createCounter = (secret) => {
|
|
|
407
421
|
//#endregion
|
|
408
422
|
export { transformCode as a, VIRTUAL_LOCAL_MODULE_ID as c, extractKeywords as i, VIRTUAL_MODULE_ID as l, createHasher as n, encodeIdentifier as o, createRunner as r, PLUGIN_NAME as s, createCounter as t };
|
|
409
423
|
|
|
410
|
-
//# sourceMappingURL=hash-
|
|
424
|
+
//# sourceMappingURL=hash-CRJe3IJz.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash-CRJe3IJz.js","names":["t","info"],"sources":["../src/internal/constants.ts","../src/internal/encode.ts","../src/internal/transform.ts","../src/internal/typegen.ts","../src/internal/cli.ts","../src/internal/hash.ts"],"sourcesContent":["/**\n * @license\n * SPDX-License-Identifier: MIT\n */\n\nexport const VIRTUAL_MODULE_ID = '~keywords';\nexport const VIRTUAL_LOCAL_MODULE_ID = '~keywords/local';\n\nexport const PLUGIN_NAME = 'unplugin-keywords';\n\nexport const HASH_LENGTH = 7;\n\nexport const KEYWORD_ROUTE_SEGMENT = '_';\n\n// URL-safe so that K.abc can be used in URL\nexport const DEBUG_SEPARATOR = '.';\n","/**\n * @license\n * SPDX-License-Identifier: MIT\n */\n\ndeclare const __encoded__: unique symbol;\ntype Encoded = string & { [__encoded__]: never };\n\nexport const encodeIdentifier = (identifier: string): Encoded => {\n let encoded = '';\n for (let i = 0; i < identifier.length; i++) {\n const c = identifier[i] as string;\n if (/[a-zA-Z0-9_]/.test(c)) {\n encoded += c;\n } else if (c === '$') {\n encoded += '$$';\n } else {\n encoded += `$${c.charCodeAt(0).toString(16).padStart(4, '0')}`;\n }\n }\n return encoded as Encoded;\n};\n\nexport const toSafeVarName = (encoded: Encoded): string => `_$${encoded}`;\n","/**\n * @license\n * SPDX-License-Identifier: MIT\n */\n\nimport {\n type BabelFileResult,\n type NodePath,\n type PluginObj,\n type PluginPass,\n types as t,\n transformSync,\n} from '@babel/core';\nimport {\n KEYWORD_ROUTE_SEGMENT,\n PLUGIN_NAME,\n VIRTUAL_LOCAL_MODULE_ID,\n VIRTUAL_MODULE_ID,\n} from './constants.js';\nimport { encodeIdentifier, toSafeVarName } from './encode.js';\n\nexport interface KeywordSet {\n main: Set<string>;\n local: Set<string>;\n}\n\nconst isPureTypeSpace = (path: NodePath): boolean => {\n let current: NodePath | null = path;\n while (current) {\n const parent = current.parentPath;\n if (!parent) {\n break;\n }\n // 1. Value crossings via `typeof`\n if (parent.isTSTypeQuery()) {\n return false;\n }\n // 2. Computed keys (e.g., interface I { [Abc]: string })\n if ('computed' in parent.node && parent.node.computed) {\n if (current.key === 'key' || current.key === 'property') {\n return false;\n }\n }\n // 3-A. Definitive Type Contexts\n if (\n parent.isTSType() ||\n parent.isTSTypeParameterDeclaration() ||\n parent.isTSTypeParameterInstantiation() ||\n parent.isTSExpressionWithTypeArguments()\n ) {\n return true;\n }\n // 3-B. Type Declaration Identifiers (e.g., interface Abc {}, type Abc = {})\n if (\n parent.isTSInterfaceDeclaration() ||\n parent.isTSTypeAliasDeclaration() ||\n parent.isTSEnumDeclaration() ||\n parent.isTSModuleDeclaration()\n ) {\n if (current.key === 'id') {\n return true;\n }\n }\n // 4. Continue up structural TS nodes (A.B.C)\n if (parent.isTSQualifiedName() || parent.isTSEntityName()) {\n current = current.parentPath;\n continue;\n }\n // 5. If we reach standard JS statements/expressions, it implies Value Space.\n if (parent.isExpression() || parent.isStatement()) {\n break;\n }\n current = current.parentPath;\n }\n return false;\n};\n\ninterface TransformState extends PluginPass {\n keywords: KeywordSet;\n keywordUids: {\n main: Map<string, t.Identifier>;\n local: Map<string, t.Identifier>;\n };\n}\n\ninterface TransformMetadata {\n keywords?: { main: string[]; local: string[] };\n}\n\nconst transformPlugin = (\n mode: 'extract' | 'transform',\n): PluginObj<TransformState> => {\n return {\n name: `${PLUGIN_NAME}:${mode}`,\n\n visitor: {\n Program: {\n enter(_, state) {\n state.keywords = { main: new Set(), local: new Set() };\n state.keywordUids = { main: new Map(), local: new Map() };\n },\n\n exit(path, state) {\n const metadata = state.file.metadata as TransformMetadata;\n metadata.keywords = {\n main: Array.from(state.keywords.main),\n local: Array.from(state.keywords.local),\n };\n\n if (mode === 'transform') {\n const newImports = [];\n for (const [keyword, safeId] of state.keywordUids.main.entries()) {\n const encoded = encodeIdentifier(keyword);\n newImports.push(\n t.importDeclaration(\n [t.importDefaultSpecifier(safeId)],\n t.stringLiteral(\n `${VIRTUAL_MODULE_ID}/${KEYWORD_ROUTE_SEGMENT}/${encoded}`,\n ),\n ),\n );\n }\n for (const [keyword, safeId] of state.keywordUids.local.entries()) {\n const encoded = encodeIdentifier(keyword);\n newImports.push(\n t.importDeclaration(\n [t.importDefaultSpecifier(safeId)],\n t.stringLiteral(\n `${VIRTUAL_LOCAL_MODULE_ID}/${KEYWORD_ROUTE_SEGMENT}/${encoded}`,\n ),\n ),\n );\n }\n if (newImports.length > 0) {\n path.unshiftContainer('body', newImports);\n }\n }\n },\n },\n\n ImportDeclaration(path, state) {\n const sourceValue = path.node.source.value;\n if (\n sourceValue !== VIRTUAL_MODULE_ID &&\n sourceValue !== VIRTUAL_LOCAL_MODULE_ID\n ) {\n return;\n }\n const isLocal = sourceValue === VIRTUAL_LOCAL_MODULE_ID;\n const targetSet = isLocal ? state.keywords.local : state.keywords.main;\n const targetMap = isLocal\n ? state.keywordUids.local\n : state.keywordUids.main;\n\n const programScope = path.scope.getProgramParent();\n const processKeyword = (keyword: string): t.Identifier | null => {\n targetSet.add(keyword);\n if (mode === 'extract') {\n return null;\n }\n if (targetMap.has(keyword)) {\n return targetMap.get(keyword) as t.Identifier;\n }\n const encoded = encodeIdentifier(keyword);\n const safeName = toSafeVarName(encoded);\n const uid = programScope.generateUidIdentifier(safeName);\n targetMap.set(keyword, uid);\n return uid;\n };\n\n for (const specifierPath of path.get('specifiers')) {\n const localName = specifierPath.node.local.name;\n const binding = path.scope.getBinding(localName);\n if (!binding) {\n continue;\n }\n\n // Case A: Default & Named Imports\n if (\n specifierPath.isImportDefaultSpecifier() ||\n specifierPath.isImportSpecifier()\n ) {\n let keyword: string;\n if (specifierPath.isImportDefaultSpecifier()) {\n keyword = 'default';\n } else {\n const imported = specifierPath.node.imported;\n keyword = t.isIdentifier(imported)\n ? imported.name\n : imported.value;\n }\n const uidNode = processKeyword(keyword);\n if (!uidNode) {\n continue;\n }\n\n // 1) Fast Path: Values & JSX\n for (const refPath of binding.referencePaths) {\n if (isPureTypeSpace(refPath)) {\n continue;\n }\n if (refPath.isJSXIdentifier()) {\n refPath.replaceWith(t.jsxIdentifier(uidNode.name));\n } else {\n refPath.replaceWith(t.cloneNode(uidNode));\n }\n }\n\n // 2) Slow Path: TS Types\n // NOTE: Can be skipped due to type erasure, but for consistency\n path.parentPath.traverse({\n // e.g., type T = typeof abc;\n TSTypeQuery(tsPath) {\n if (\n t.isIdentifier(tsPath.node.exprName) &&\n tsPath.node.exprName.name === localName &&\n tsPath.scope.getBinding(localName) === binding\n ) {\n tsPath.get('exprName').replaceWith(t.cloneNode(uidNode));\n }\n },\n });\n }\n\n // Case B: Namespace Imports\n else if (specifierPath.isImportNamespaceSpecifier()) {\n // 1) Fast Path: JS Values & JSX accesses\n for (const refPath of binding.referencePaths) {\n if (isPureTypeSpace(refPath)) {\n continue;\n }\n const parentPath = refPath.parentPath;\n if (!parentPath) {\n continue;\n }\n if (\n parentPath.isMemberExpression() &&\n parentPath.node.object === refPath.node\n ) {\n const propNode = parentPath.node.property;\n let keyword: string | undefined;\n if (!parentPath.node.computed && t.isIdentifier(propNode)) {\n keyword = propNode.name;\n } else if (\n parentPath.node.computed &&\n t.isStringLiteral(propNode)\n ) {\n keyword = propNode.value;\n }\n if (keyword) {\n const uidNode = processKeyword(keyword);\n if (uidNode) {\n parentPath.replaceWith(t.cloneNode(uidNode));\n }\n }\n } else if (\n parentPath.isJSXMemberExpression() &&\n parentPath.node.object === refPath.node\n ) {\n const keyword = parentPath.node.property.name;\n const uidNode = processKeyword(keyword);\n if (uidNode) {\n parentPath.replaceWith(t.jsxIdentifier(uidNode.name));\n }\n }\n }\n\n // 2) Slow Path: TS Namespace Types\n path.parentPath.traverse({\n // e.g., type T = typeof A.abc;\n TSTypeQuery(tsPath) {\n const expr = tsPath.node.exprName;\n if (\n t.isTSQualifiedName(expr) &&\n t.isIdentifier(expr.left) &&\n expr.left.name === localName &&\n tsPath.scope.getBinding(localName) === binding\n ) {\n const keyword = expr.right.name;\n const uidNode = processKeyword(keyword);\n if (uidNode) {\n tsPath.get('exprName').replaceWith(t.cloneNode(uidNode));\n }\n }\n },\n\n // e.g., type T = ((typeof A))['prop'];\n TSIndexedAccessType(tsPath) {\n let objPath = tsPath.get('objectType') as NodePath;\n // Unpack highly nested parentheses gracefully\n while (objPath.isTSParenthesizedType()) {\n objPath = objPath.get('typeAnnotation') as NodePath;\n }\n if (\n objPath.isTSTypeQuery() &&\n t.isIdentifier(objPath.node.exprName) &&\n objPath.node.exprName.name === localName &&\n tsPath.scope.getBinding(localName) === binding\n ) {\n const indexNode = tsPath.node.indexType;\n if (\n t.isTSLiteralType(indexNode) &&\n t.isStringLiteral(indexNode.literal)\n ) {\n const keyword = indexNode.literal.value;\n const uidNode = processKeyword(keyword);\n if (uidNode) {\n tsPath.replaceWith(t.tsTypeQuery(t.cloneNode(uidNode)));\n }\n }\n }\n },\n });\n }\n }\n\n if (mode === 'transform') {\n path.remove();\n }\n },\n\n ExportNamedDeclaration(path, state) {\n const sourceValue = path.node.source?.value;\n if (\n sourceValue !== VIRTUAL_MODULE_ID &&\n sourceValue !== VIRTUAL_LOCAL_MODULE_ID\n ) {\n return;\n }\n const isLocal = sourceValue === VIRTUAL_LOCAL_MODULE_ID;\n const targetSet = isLocal ? state.keywords.local : state.keywords.main;\n\n if (mode === 'extract') {\n for (const specifierPath of path.get('specifiers')) {\n if (specifierPath.isExportSpecifier()) {\n const local = specifierPath.node.local as\n | t.Identifier\n | t.StringLiteral; // local can be a StringLiteral in ES2022\n const keyword = t.isIdentifier(local) ? local.name : local.value;\n targetSet.add(keyword);\n }\n }\n return;\n }\n\n const newExports = path\n .get('specifiers')\n .map((specifierPath) => {\n if (specifierPath.isExportSpecifier()) {\n const local = specifierPath.node.local as\n | t.Identifier\n | t.StringLiteral; // local can be a StringLiteral in ES2022\n const keyword = t.isIdentifier(local) ? local.name : local.value;\n targetSet.add(keyword);\n const encoded = encodeIdentifier(keyword);\n return t.exportNamedDeclaration(\n null,\n [\n t.exportSpecifier(\n t.identifier('default'),\n specifierPath.node.exported,\n ),\n ],\n t.stringLiteral(\n `${sourceValue}/${KEYWORD_ROUTE_SEGMENT}/${encoded}`,\n ),\n );\n }\n return null;\n })\n .filter((node): node is t.ExportNamedDeclaration => node !== null);\n\n if (newExports.length > 0) {\n path.replaceWithMultiple(newExports);\n } else {\n path.remove();\n }\n },\n },\n };\n};\n\nexport const transformCode = (\n code: string,\n id: string,\n): {\n code: string;\n map: NonNullable<BabelFileResult['map']> | null;\n keywords: KeywordSet;\n} | null => {\n if (\n !code.includes(VIRTUAL_MODULE_ID) &&\n !code.includes(VIRTUAL_LOCAL_MODULE_ID)\n ) {\n return null;\n }\n const result = transformSync(code, {\n babelrc: false,\n configFile: false,\n filename: id,\n sourceMaps: true,\n ast: false,\n plugins: [transformPlugin('transform')],\n parserOpts: {\n plugins: ['jsx', 'typescript'],\n },\n });\n if (!result) {\n return null;\n }\n const metadata = result.metadata as TransformMetadata | undefined;\n const keywords: KeywordSet = {\n main: new Set(metadata?.keywords?.main ?? []),\n local: new Set(metadata?.keywords?.local ?? []),\n };\n return {\n code: result.code ?? '',\n map: result.map ?? null,\n keywords,\n };\n};\n\nexport const extractKeywords = (code: string): KeywordSet | null => {\n if (\n !code.includes(VIRTUAL_MODULE_ID) &&\n !code.includes(VIRTUAL_LOCAL_MODULE_ID)\n ) {\n return null;\n }\n let result: BabelFileResult | null;\n try {\n result = transformSync(code, {\n babelrc: false,\n configFile: false,\n sourceMaps: false,\n ast: false,\n code: false,\n plugins: [transformPlugin('extract')],\n parserOpts: {\n plugins: ['jsx', 'typescript'],\n errorRecovery: true,\n },\n });\n } catch {\n return null;\n }\n if (!result) {\n return null;\n }\n const metadata = result.metadata as TransformMetadata | undefined;\n return {\n main: new Set(metadata?.keywords?.main ?? []),\n local: new Set(metadata?.keywords?.local ?? []),\n };\n};\n","/**\n * @license\n * SPDX-License-Identifier: MIT\n */\n\nimport { DEBUG_SEPARATOR, HASH_LENGTH } from './constants.js';\nimport { encodeIdentifier, toSafeVarName } from './encode.js';\n\nexport const generateTypeDeclaration = (\n keywords: Set<string>,\n isLocal: boolean = false,\n): string => {\n const sortedKeywords = Array.from(keywords).sort();\n const content = [];\n\n for (const keyword of sortedKeywords) {\n const encoded = encodeIdentifier(keyword);\n const safeName = toSafeVarName(encoded);\n const hash = isLocal ? '==' : '*'.repeat(HASH_LENGTH);\n const value = `${hash}${DEBUG_SEPARATOR}${keyword}`;\n content.push(`declare const ${safeName}: ${JSON.stringify(value)};`);\n }\n content.push('');\n\n content.push('export {');\n for (const keyword of sortedKeywords) {\n const encoded = encodeIdentifier(keyword);\n const safeName = toSafeVarName(encoded);\n content.push(` ${safeName} as ${JSON.stringify(keyword)},`);\n }\n content.push('};');\n content.push('');\n\n return content.join('\\n');\n};\n","/**\n * @license\n * SPDX-License-Identifier: MIT\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { globby } from 'globby';\nimport pLimit from 'p-limit';\nimport { KEYWORD_ROUTE_SEGMENT } from './constants.js';\nimport { extractKeywords, type KeywordSet } from './transform.js';\nimport { generateTypeDeclaration } from './typegen.js';\n\nconst collectKeywordsFromRoot = async (\n root: string,\n silent: boolean,\n ignoredDirs: string[] = [],\n concurrency: number = 100,\n): Promise<KeywordSet> => {\n const collectedKeywords: KeywordSet = { main: new Set(), local: new Set() };\n\n const start = performance.now();\n if (!silent) {\n console.error('Scanning project files for keywords...');\n }\n\n const files = await globby('**/*.{js,ts,mjs,mts,jsx,tsx,mjsx,mtsx}', {\n cwd: root,\n absolute: false,\n ignore: ['**/node_modules/**', ...ignoredDirs.map((dir) => `${dir}/**`)],\n gitignore: true,\n });\n\n let processed = 0;\n const limit = pLimit({ concurrency });\n await limit.map(files, async (file) => {\n try {\n const code = await readFile(file, 'utf-8');\n const keywords = extractKeywords(code);\n if (!keywords) {\n return;\n }\n for (const keyword of keywords.main) {\n collectedKeywords.main.add(keyword);\n }\n for (const keyword of keywords.local) {\n collectedKeywords.local.add(keyword);\n }\n processed++;\n } catch (error) {\n if (!silent) {\n console.error(`Failed to process ${file}: ${error}`);\n }\n }\n });\n\n const elapsed = performance.now() - start;\n if (!silent) {\n console.error(\n `Scan complete: ${processed}/${files.length} files, ${collectedKeywords.main.size} main, ${collectedKeywords.local.size} local keywords (${elapsed.toFixed(2)}ms).`,\n );\n }\n\n return collectedKeywords;\n};\n\nconst pkgJson = {\n name: '~keywords',\n version: '0.0.0',\n private: true,\n type: 'module',\n sideEffects: false,\n exports: {\n '.': {\n types: './index.d.ts',\n },\n './local': {\n types: './local.d.ts',\n },\n [`./${KEYWORD_ROUTE_SEGMENT}/*`]: `./${KEYWORD_ROUTE_SEGMENT}/*`,\n [`./local/${KEYWORD_ROUTE_SEGMENT}/*`]: `./local/${KEYWORD_ROUTE_SEGMENT}/*`,\n },\n};\n\ninterface RunnerOptions {\n root: string;\n silent: boolean;\n outDir: string;\n}\n\nexport const createRunner = (options?: Partial<RunnerOptions>) => {\n const {\n root = process.cwd(),\n silent = false,\n outDir = path.join('node_modules', '~keywords'),\n } = options ?? {};\n return {\n async collect(): Promise<KeywordSet> {\n return collectKeywordsFromRoot(root, silent);\n },\n\n async save(keywords: KeywordSet): Promise<void> {\n const content = generateTypeDeclaration(keywords.main);\n const localContent = generateTypeDeclaration(keywords.local, true);\n const outPath = path.join(root, outDir);\n await mkdir(outPath, { recursive: true });\n await writeFile(path.join(outPath, 'index.d.ts'), `${content.trim()}\\n`);\n await writeFile(\n path.join(outPath, 'local.d.ts'),\n `${localContent.trim()}\\n`,\n );\n await writeFile(\n path.join(outPath, 'package.json'),\n `${JSON.stringify(pkgJson, null, 2)}\\n`,\n );\n },\n\n async run(): Promise<void> {\n const keywords = await this.collect();\n await this.save(keywords);\n },\n };\n};\n","/**\n * @license\n * SPDX-License-Identifier: MIT\n */\n\nimport { createHmac, hkdfSync } from 'node:crypto';\nimport {\n HASH_LENGTH,\n VIRTUAL_LOCAL_MODULE_ID,\n VIRTUAL_MODULE_ID,\n} from './constants.js';\n\nconst ALPHA_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\nconst DIGIT_CHARS = '0123456789';\nconst BASE62_CHARS = ALPHA_CHARS + DIGIT_CHARS;\n\nconst ALPHA_LEN = BigInt(ALPHA_CHARS.length); // 52n\nconst DIGIT_LEN = BigInt(DIGIT_CHARS.length); // 10n\nconst BASE62_LEN = BigInt(BASE62_CHARS.length); // 62n\n\nexport type Hasher = (input: string) => string;\n\n// Format: [1 Alpha] + [1 Digit] + [N Base62]\n// Avoids any collisions with standard JS API identifiers\nexport const createHasher = (secret: string): Hasher => {\n const base62TailLength = HASH_LENGTH - 2;\n if (base62TailLength < 0 || base62TailLength > 9) {\n // 520 * 62^9 < 2^64 < 520 * 62^10\n throw new Error('Invalid MAX_HASH_LENGTH');\n }\n\n const cache = new Map<string, string>();\n return (input) => {\n if (cache.has(input)) {\n return cache.get(input) as string;\n }\n\n const info = VIRTUAL_MODULE_ID;\n const payload = `${info.length}:${info}|${input.length}:${input}`;\n const hasher = createHmac('sha256', secret);\n const buffer = hasher.update(payload).digest('hex');\n\n let entropy = BigInt(`0x${buffer}`);\n let result = '';\n\n result += ALPHA_CHARS[Number(entropy % ALPHA_LEN)];\n entropy /= ALPHA_LEN;\n result += DIGIT_CHARS[Number(entropy % DIGIT_LEN)];\n entropy /= DIGIT_LEN;\n for (let i = 0; i < base62TailLength; i++) {\n result += BASE62_CHARS[Number(entropy % BASE62_LEN)];\n entropy /= BASE62_LEN;\n }\n\n cache.set(input, result);\n return result;\n };\n};\n\n// Fisher-Yates based deterministic shuffle\nconst shuffle = (str: string, secret: string, salt: string): string => {\n const arr = Array.from(str);\n const requiredBytes = (arr.length - 1) * 4;\n\n const info = VIRTUAL_LOCAL_MODULE_ID;\n const keyingMaterial = hkdfSync('sha256', secret, salt, info, requiredBytes);\n const prngBuffer = Buffer.from(keyingMaterial);\n\n let byteOffset = 0;\n for (let i = arr.length - 1; i > 0; i--) {\n const random32 = prngBuffer.readUInt32BE(byteOffset);\n byteOffset += 4;\n const j = random32 % (i + 1);\n const temp = arr[i] as string;\n arr[i] = arr[j] as string;\n arr[j] = temp;\n }\n\n return arr.join('');\n};\n\nexport const createCounter = (secret: string): Hasher => {\n const shuffledAlpha = shuffle(ALPHA_CHARS, secret, 'alpha');\n const shuffledDigit = shuffle(DIGIT_CHARS, secret, 'digit');\n const shuffledBase62 = shuffle(BASE62_CHARS, secret, 'base62');\n\n let index = 0;\n const cache = new Map<string, string>();\n return (input) => {\n if (cache.has(input)) {\n return cache.get(input) as string;\n }\n\n let result = '';\n let current = index;\n index++;\n\n result += shuffledAlpha[current % shuffledAlpha.length];\n current = Math.floor(current / shuffledAlpha.length);\n result += shuffledDigit[current % shuffledDigit.length];\n current = Math.floor(current / shuffledDigit.length);\n while (current > 0) {\n current--;\n result += shuffledBase62[current % shuffledBase62.length];\n current = Math.floor(current / shuffledBase62.length);\n }\n\n cache.set(input, result);\n return result;\n };\n};\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as transformCode, c as VIRTUAL_LOCAL_MODULE_ID, i as extractKeywords, l as VIRTUAL_MODULE_ID, n as createHasher, o as encodeIdentifier, r as createRunner, s as PLUGIN_NAME, t as createCounter } from "./hash-
|
|
1
|
+
import { a as transformCode, c as VIRTUAL_LOCAL_MODULE_ID, i as extractKeywords, l as VIRTUAL_MODULE_ID, n as createHasher, o as encodeIdentifier, r as createRunner, s as PLUGIN_NAME, t as createCounter } from "./hash-CRJe3IJz.js";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import pLimit from "p-limit";
|
|
4
4
|
//#region src/internal/plugin.ts
|
|
@@ -128,4 +128,4 @@ const unpluginFactory = ({ isDev, secret }) => {
|
|
|
128
128
|
//#endregion
|
|
129
129
|
export { unpluginFactory as t };
|
|
130
130
|
|
|
131
|
-
//# sourceMappingURL=plugin-
|
|
131
|
+
//# sourceMappingURL=plugin-DZh87VhX.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-
|
|
1
|
+
{"version":3,"file":"plugin-DZh87VhX.js","names":[],"sources":["../src/internal/plugin.ts"],"sourcesContent":["/**\n * @license\n * SPDX-License-Identifier: MIT\n */\n\nimport { readFile } from 'node:fs/promises';\nimport pLimit from 'p-limit';\nimport type { UnpluginFactory } from 'unplugin';\nimport { createRunner } from './cli.js';\nimport {\n DEBUG_SEPARATOR,\n KEYWORD_ROUTE_SEGMENT,\n PLUGIN_NAME,\n VIRTUAL_LOCAL_MODULE_ID,\n VIRTUAL_MODULE_ID,\n} from './constants.js';\nimport { encodeIdentifier } from './encode.js';\nimport { createCounter, createHasher, type Hasher } from './hash.js';\nimport {\n extractKeywords,\n type KeywordSet,\n transformCode,\n} from './transform.js';\n\nconst resolveId = (id: string): string => `\\0${id}`;\n\nconst splitQuery = (id: string): [string, string | undefined] => {\n const index = id.indexOf('?');\n if (index === -1) {\n return [id, undefined];\n }\n return [id.slice(0, index), id.slice(index + 1)];\n};\n\nconst toIncludes = (id: string): RegExp[] => [new RegExp(`^${id}/`)];\n\nconst SUFFIX_REGEX = /\\.m?[jt]sx?$/;\nconst COMMON_EXCLUDES = [/\\/node_modules\\//];\n\nexport interface Options {\n /**\n * If true, preserves the original keyword as a suffix in the generated\n * identifier for easier debugging (e.g., `\"zXpL21k.SET_USER\"`).\n */\n isDev: boolean;\n /**\n * The cryptographic key used to initialize the deterministic HMAC algorithm.\n * Modifying this value will globally rotate all generated hashes.\n * To ensure cross-boundary consistency between independent builds,\n * they must share the same secret key.\n */\n secret: string;\n}\n\nexport const unpluginFactory: UnpluginFactory<Options> = ({\n isDev,\n secret,\n}) => {\n const runner = createRunner({ silent: true });\n const runnerLimit = pLimit({ concurrency: 1 });\n const typegenKeywords: KeywordSet = { main: new Set(), local: new Set() };\n\n let isInitialized = false;\n const runInit = async () => {\n try {\n const keywords = await runner.collect();\n for (const keyword of keywords.main) {\n typegenKeywords.main.add(keyword);\n }\n for (const keyword of keywords.local) {\n typegenKeywords.local.add(keyword);\n }\n await runner.save(typegenKeywords);\n isInitialized = true;\n } catch {}\n };\n\n let hasherMain: Hasher;\n let hasherLocal: Hasher;\n let resolvedMap: Map<string, string>;\n\n return {\n name: PLUGIN_NAME,\n\n buildStart() {\n hasherMain = createHasher(secret);\n hasherLocal = createCounter(secret);\n resolvedMap = new Map();\n runnerLimit(async () => {\n if (!isInitialized) {\n await runInit();\n }\n });\n },\n\n resolveId: {\n filter: {\n id: {\n include: [\n ...toIncludes(VIRTUAL_MODULE_ID),\n ...toIncludes(VIRTUAL_LOCAL_MODULE_ID),\n ],\n exclude: COMMON_EXCLUDES,\n },\n },\n handler(id) {\n return resolveId(id);\n },\n },\n\n load: {\n filter: {\n id: {\n include: [\n ...toIncludes(resolveId(VIRTUAL_MODULE_ID)),\n ...toIncludes(resolveId(VIRTUAL_LOCAL_MODULE_ID)),\n ],\n exclude: COMMON_EXCLUDES,\n },\n },\n handler(id) {\n const [validId] = splitQuery(id);\n if (resolvedMap.has(validId)) {\n return resolvedMap.get(validId);\n }\n return null;\n },\n },\n\n transform: {\n filter: {\n id: {\n include: [SUFFIX_REGEX],\n exclude: COMMON_EXCLUDES,\n },\n code: {\n include: [VIRTUAL_MODULE_ID, VIRTUAL_LOCAL_MODULE_ID],\n },\n },\n handler(code, id) {\n const result = transformCode(code, id);\n if (!result) {\n return null;\n }\n const { code: transformed, map, keywords } = result;\n for (const keyword of keywords.main) {\n const encoded = encodeIdentifier(keyword);\n const resolvedId = resolveId(\n `${VIRTUAL_MODULE_ID}/${KEYWORD_ROUTE_SEGMENT}/${encoded}`,\n );\n if (resolvedMap.has(resolvedId)) {\n continue;\n }\n const hash = hasherMain(keyword);\n const value = isDev ? `${hash}${DEBUG_SEPARATOR}${keyword}` : hash;\n resolvedMap.set(\n resolvedId,\n `export default ${JSON.stringify(value)};\\n`,\n );\n }\n for (const keyword of keywords.local) {\n const encoded = encodeIdentifier(keyword);\n const resolvedId = resolveId(\n `${VIRTUAL_LOCAL_MODULE_ID}/${KEYWORD_ROUTE_SEGMENT}/${encoded}`,\n );\n if (resolvedMap.has(resolvedId)) {\n continue;\n }\n const hash = hasherLocal(keyword);\n const value = isDev ? `${hash}${DEBUG_SEPARATOR}${keyword}` : hash;\n resolvedMap.set(\n resolvedId,\n `export default ${JSON.stringify(value)};\\n`,\n );\n }\n return { code: transformed, map };\n },\n },\n\n async watchChange(id, { event }) {\n if (\n !SUFFIX_REGEX.test(id) ||\n COMMON_EXCLUDES.some((regex) => regex.test(id)) ||\n event === 'delete'\n ) {\n return;\n }\n let code: string;\n try {\n code = await readFile(id, 'utf-8');\n } catch {\n return;\n }\n const keywords = extractKeywords(code);\n if (!keywords) {\n return;\n }\n let isAdded = false;\n for (const keyword of keywords.main) {\n if (!typegenKeywords.main.has(keyword)) {\n typegenKeywords.main.add(keyword);\n isAdded = true;\n }\n }\n for (const keyword of keywords.local) {\n if (!typegenKeywords.local.has(keyword)) {\n typegenKeywords.local.add(keyword);\n isAdded = true;\n }\n }\n if (!isInitialized || isAdded) {\n runnerLimit(async () => {\n if (!isInitialized) {\n await runInit();\n } else if (isAdded) {\n try {\n await runner.save(typegenKeywords);\n } catch {}\n }\n });\n }\n },\n };\n};\n"],"mappings":"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"}
|
package/dist/rollup.js
CHANGED
package/dist/vite.js
CHANGED
package/dist/webpack.js
CHANGED
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hash-DVFA0jSt.js","names":["t","info"],"sources":["../src/internal/constants.ts","../src/internal/encode.ts","../src/internal/transform.ts","../src/internal/typegen.ts","../src/internal/cli.ts","../src/internal/hash.ts"],"sourcesContent":["/**\n * @license\n * SPDX-License-Identifier: MIT\n */\n\nexport const VIRTUAL_MODULE_ID = 'virtual:keywords';\nexport const VIRTUAL_LOCAL_MODULE_ID = 'virtual:keywords/local';\n\nexport const PLUGIN_NAME = 'unplugin-keywords';\n\nexport const HASH_LENGTH = 7;\n\nexport const KEYWORD_ROUTE_SEGMENT = '_';\n\n// URL-safe so that K.abc can be used in URL\nexport const DEBUG_SEPARATOR = '.';\n","/**\n * @license\n * SPDX-License-Identifier: MIT\n */\n\ndeclare const __encoded__: unique symbol;\ntype Encoded = string & { [__encoded__]: never };\n\nexport const encodeIdentifier = (identifier: string): Encoded => {\n let encoded = '';\n for (let i = 0; i < identifier.length; i++) {\n const c = identifier[i] as string;\n if (/[a-zA-Z0-9_]/.test(c)) {\n encoded += c;\n } else if (c === '$') {\n encoded += '$$';\n } else {\n encoded += `$${c.charCodeAt(0).toString(16).padStart(4, '0')}`;\n }\n }\n return encoded as Encoded;\n};\n\nexport const toSafeVarName = (encoded: Encoded): string => `_$${encoded}`;\n","/**\n * @license\n * SPDX-License-Identifier: MIT\n */\n\nimport {\n type BabelFileResult,\n type NodePath,\n type PluginObj,\n type PluginPass,\n types as t,\n transformSync,\n} from '@babel/core';\nimport {\n KEYWORD_ROUTE_SEGMENT,\n PLUGIN_NAME,\n VIRTUAL_LOCAL_MODULE_ID,\n VIRTUAL_MODULE_ID,\n} from './constants.js';\nimport { encodeIdentifier, toSafeVarName } from './encode.js';\n\nexport interface KeywordSet {\n main: Set<string>;\n local: Set<string>;\n}\n\nconst isPureTypeSpace = (path: NodePath): boolean => {\n let current: NodePath | null = path;\n while (current) {\n const parent = current.parentPath;\n if (!parent) {\n break;\n }\n // 1. Value crossings via `typeof`\n if (parent.isTSTypeQuery()) {\n return false;\n }\n // 2. Computed keys (e.g., interface I { [Abc]: string })\n if ('computed' in parent.node && parent.node.computed) {\n if (current.key === 'key' || current.key === 'property') {\n return false;\n }\n }\n // 3-A. Definitive Type Contexts\n if (\n parent.isTSType() ||\n parent.isTSTypeParameterDeclaration() ||\n parent.isTSTypeParameterInstantiation() ||\n parent.isTSExpressionWithTypeArguments()\n ) {\n return true;\n }\n // 3-B. Type Declaration Identifiers (e.g., interface Abc {}, type Abc = {})\n if (\n parent.isTSInterfaceDeclaration() ||\n parent.isTSTypeAliasDeclaration() ||\n parent.isTSEnumDeclaration() ||\n parent.isTSModuleDeclaration()\n ) {\n if (current.key === 'id') {\n return true;\n }\n }\n // 4. Continue up structural TS nodes (A.B.C)\n if (parent.isTSQualifiedName() || parent.isTSEntityName()) {\n current = current.parentPath;\n continue;\n }\n // 5. If we reach standard JS statements/expressions, it implies Value Space.\n if (parent.isExpression() || parent.isStatement()) {\n break;\n }\n current = current.parentPath;\n }\n return false;\n};\n\ninterface TransformState extends PluginPass {\n keywords: KeywordSet;\n keywordUids: {\n main: Map<string, t.Identifier>;\n local: Map<string, t.Identifier>;\n };\n}\n\ninterface TransformMetadata {\n keywords?: { main: string[]; local: string[] };\n}\n\nconst transformPlugin = (\n mode: 'extract' | 'transform',\n): PluginObj<TransformState> => {\n return {\n name: `${PLUGIN_NAME}:${mode}`,\n\n visitor: {\n Program: {\n enter(_, state) {\n state.keywords = { main: new Set(), local: new Set() };\n state.keywordUids = { main: new Map(), local: new Map() };\n },\n\n exit(path, state) {\n const metadata = state.file.metadata as TransformMetadata;\n metadata.keywords = {\n main: Array.from(state.keywords.main),\n local: Array.from(state.keywords.local),\n };\n\n if (mode === 'transform') {\n const newImports = [];\n for (const [keyword, safeId] of state.keywordUids.main.entries()) {\n const encoded = encodeIdentifier(keyword);\n newImports.push(\n t.importDeclaration(\n [t.importDefaultSpecifier(safeId)],\n t.stringLiteral(\n `${VIRTUAL_MODULE_ID}/${KEYWORD_ROUTE_SEGMENT}/${encoded}`,\n ),\n ),\n );\n }\n for (const [keyword, safeId] of state.keywordUids.local.entries()) {\n const encoded = encodeIdentifier(keyword);\n newImports.push(\n t.importDeclaration(\n [t.importDefaultSpecifier(safeId)],\n t.stringLiteral(\n `${VIRTUAL_LOCAL_MODULE_ID}/${KEYWORD_ROUTE_SEGMENT}/${encoded}`,\n ),\n ),\n );\n }\n if (newImports.length > 0) {\n path.unshiftContainer('body', newImports);\n }\n }\n },\n },\n\n ImportDeclaration(path, state) {\n const sourceValue = path.node.source.value;\n if (\n sourceValue !== VIRTUAL_MODULE_ID &&\n sourceValue !== VIRTUAL_LOCAL_MODULE_ID\n ) {\n return;\n }\n const isLocal = sourceValue === VIRTUAL_LOCAL_MODULE_ID;\n const targetSet = isLocal ? state.keywords.local : state.keywords.main;\n const targetMap = isLocal\n ? state.keywordUids.local\n : state.keywordUids.main;\n\n const programScope = path.scope.getProgramParent();\n const processKeyword = (keyword: string): t.Identifier | null => {\n targetSet.add(keyword);\n if (mode === 'extract') {\n return null;\n }\n if (targetMap.has(keyword)) {\n return targetMap.get(keyword) as t.Identifier;\n }\n const encoded = encodeIdentifier(keyword);\n const safeName = toSafeVarName(encoded);\n const uid = programScope.generateUidIdentifier(safeName);\n targetMap.set(keyword, uid);\n return uid;\n };\n\n for (const specifierPath of path.get('specifiers')) {\n const localName = specifierPath.node.local.name;\n const binding = path.scope.getBinding(localName);\n if (!binding) {\n continue;\n }\n\n // Case A: Default & Named Imports\n if (\n specifierPath.isImportDefaultSpecifier() ||\n specifierPath.isImportSpecifier()\n ) {\n let keyword: string;\n if (specifierPath.isImportDefaultSpecifier()) {\n keyword = 'default';\n } else {\n const imported = specifierPath.node.imported;\n keyword = t.isIdentifier(imported)\n ? imported.name\n : imported.value;\n }\n const uidNode = processKeyword(keyword);\n if (!uidNode) {\n continue;\n }\n\n // 1) Fast Path: Values & JSX\n for (const refPath of binding.referencePaths) {\n if (isPureTypeSpace(refPath)) {\n continue;\n }\n if (refPath.isJSXIdentifier()) {\n refPath.replaceWith(t.jsxIdentifier(uidNode.name));\n } else {\n refPath.replaceWith(t.cloneNode(uidNode));\n }\n }\n\n // 2) Slow Path: TS Types\n // NOTE: Can be skipped due to type erasure, but for consistency\n path.parentPath.traverse({\n // e.g., type T = typeof abc;\n TSTypeQuery(tsPath) {\n if (\n t.isIdentifier(tsPath.node.exprName) &&\n tsPath.node.exprName.name === localName &&\n tsPath.scope.getBinding(localName) === binding\n ) {\n tsPath.get('exprName').replaceWith(t.cloneNode(uidNode));\n }\n },\n });\n }\n\n // Case B: Namespace Imports\n else if (specifierPath.isImportNamespaceSpecifier()) {\n // 1) Fast Path: JS Values & JSX accesses\n for (const refPath of binding.referencePaths) {\n if (isPureTypeSpace(refPath)) {\n continue;\n }\n const parentPath = refPath.parentPath;\n if (!parentPath) {\n continue;\n }\n if (\n parentPath.isMemberExpression() &&\n parentPath.node.object === refPath.node\n ) {\n const propNode = parentPath.node.property;\n let keyword: string | undefined;\n if (!parentPath.node.computed && t.isIdentifier(propNode)) {\n keyword = propNode.name;\n } else if (\n parentPath.node.computed &&\n t.isStringLiteral(propNode)\n ) {\n keyword = propNode.value;\n }\n if (keyword) {\n const uidNode = processKeyword(keyword);\n if (uidNode) {\n parentPath.replaceWith(t.cloneNode(uidNode));\n }\n }\n } else if (\n parentPath.isJSXMemberExpression() &&\n parentPath.node.object === refPath.node\n ) {\n const keyword = parentPath.node.property.name;\n const uidNode = processKeyword(keyword);\n if (uidNode) {\n parentPath.replaceWith(t.jsxIdentifier(uidNode.name));\n }\n }\n }\n\n // 2) Slow Path: TS Namespace Types\n path.parentPath.traverse({\n // e.g., type T = typeof A.abc;\n TSTypeQuery(tsPath) {\n const expr = tsPath.node.exprName;\n if (\n t.isTSQualifiedName(expr) &&\n t.isIdentifier(expr.left) &&\n expr.left.name === localName &&\n tsPath.scope.getBinding(localName) === binding\n ) {\n const keyword = expr.right.name;\n const uidNode = processKeyword(keyword);\n if (uidNode) {\n tsPath.get('exprName').replaceWith(t.cloneNode(uidNode));\n }\n }\n },\n\n // e.g., type T = ((typeof A))['prop'];\n TSIndexedAccessType(tsPath) {\n let objPath = tsPath.get('objectType') as NodePath;\n // Unpack highly nested parentheses gracefully\n while (objPath.isTSParenthesizedType()) {\n objPath = objPath.get('typeAnnotation') as NodePath;\n }\n if (\n objPath.isTSTypeQuery() &&\n t.isIdentifier(objPath.node.exprName) &&\n objPath.node.exprName.name === localName &&\n tsPath.scope.getBinding(localName) === binding\n ) {\n const indexNode = tsPath.node.indexType;\n if (\n t.isTSLiteralType(indexNode) &&\n t.isStringLiteral(indexNode.literal)\n ) {\n const keyword = indexNode.literal.value;\n const uidNode = processKeyword(keyword);\n if (uidNode) {\n tsPath.replaceWith(t.tsTypeQuery(t.cloneNode(uidNode)));\n }\n }\n }\n },\n });\n }\n }\n\n if (mode === 'transform') {\n path.remove();\n }\n },\n\n ExportNamedDeclaration(path, state) {\n const sourceValue = path.node.source?.value;\n if (\n sourceValue !== VIRTUAL_MODULE_ID &&\n sourceValue !== VIRTUAL_LOCAL_MODULE_ID\n ) {\n return;\n }\n const isLocal = sourceValue === VIRTUAL_LOCAL_MODULE_ID;\n const targetSet = isLocal ? state.keywords.local : state.keywords.main;\n\n if (mode === 'extract') {\n for (const specifierPath of path.get('specifiers')) {\n if (specifierPath.isExportSpecifier()) {\n const local = specifierPath.node.local as\n | t.Identifier\n | t.StringLiteral; // local can be a StringLiteral in ES2022\n const keyword = t.isIdentifier(local) ? local.name : local.value;\n targetSet.add(keyword);\n }\n }\n return;\n }\n\n const newExports = path\n .get('specifiers')\n .map((specifierPath) => {\n if (specifierPath.isExportSpecifier()) {\n const local = specifierPath.node.local as\n | t.Identifier\n | t.StringLiteral; // local can be a StringLiteral in ES2022\n const keyword = t.isIdentifier(local) ? local.name : local.value;\n targetSet.add(keyword);\n const encoded = encodeIdentifier(keyword);\n return t.exportNamedDeclaration(\n null,\n [\n t.exportSpecifier(\n t.identifier('default'),\n specifierPath.node.exported,\n ),\n ],\n t.stringLiteral(\n `${sourceValue}/${KEYWORD_ROUTE_SEGMENT}/${encoded}`,\n ),\n );\n }\n return null;\n })\n .filter((node): node is t.ExportNamedDeclaration => node !== null);\n\n if (newExports.length > 0) {\n path.replaceWithMultiple(newExports);\n } else {\n path.remove();\n }\n },\n },\n };\n};\n\nexport const transformCode = (\n code: string,\n id: string,\n): {\n code: string;\n map: NonNullable<BabelFileResult['map']> | null;\n keywords: KeywordSet;\n} | null => {\n if (\n !code.includes(VIRTUAL_MODULE_ID) &&\n !code.includes(VIRTUAL_LOCAL_MODULE_ID)\n ) {\n return null;\n }\n const result = transformSync(code, {\n babelrc: false,\n configFile: false,\n filename: id,\n sourceMaps: true,\n ast: false,\n plugins: [transformPlugin('transform')],\n parserOpts: {\n plugins: ['jsx', 'typescript'],\n },\n });\n if (!result) {\n return null;\n }\n const metadata = result.metadata as TransformMetadata | undefined;\n const keywords: KeywordSet = {\n main: new Set(metadata?.keywords?.main ?? []),\n local: new Set(metadata?.keywords?.local ?? []),\n };\n return {\n code: result.code ?? '',\n map: result.map ?? null,\n keywords,\n };\n};\n\nexport const extractKeywords = (code: string): KeywordSet | null => {\n if (\n !code.includes(VIRTUAL_MODULE_ID) &&\n !code.includes(VIRTUAL_LOCAL_MODULE_ID)\n ) {\n return null;\n }\n let result: BabelFileResult | null;\n try {\n result = transformSync(code, {\n babelrc: false,\n configFile: false,\n sourceMaps: false,\n ast: false,\n code: false,\n plugins: [transformPlugin('extract')],\n parserOpts: {\n plugins: ['jsx', 'typescript'],\n errorRecovery: true,\n },\n });\n } catch {\n return null;\n }\n if (!result) {\n return null;\n }\n const metadata = result.metadata as TransformMetadata | undefined;\n return {\n main: new Set(metadata?.keywords?.main ?? []),\n local: new Set(metadata?.keywords?.local ?? []),\n };\n};\n","/**\n * @license\n * SPDX-License-Identifier: MIT\n */\n\nimport { DEBUG_SEPARATOR, HASH_LENGTH } from './constants.js';\nimport { encodeIdentifier, toSafeVarName } from './encode.js';\n\nexport const generateTypeDeclaration = (\n keywords: Set<string>,\n isLocal: boolean = false,\n): string => {\n const sortedKeywords = Array.from(keywords).sort();\n const content = [];\n\n for (const keyword of sortedKeywords) {\n const encoded = encodeIdentifier(keyword);\n const safeName = toSafeVarName(encoded);\n const hash = isLocal ? '==' : '*'.repeat(HASH_LENGTH);\n const value = `${hash}${DEBUG_SEPARATOR}${keyword}`;\n content.push(`declare const ${safeName}: ${JSON.stringify(value)};`);\n }\n content.push('');\n\n content.push('export {');\n for (const keyword of sortedKeywords) {\n const encoded = encodeIdentifier(keyword);\n const safeName = toSafeVarName(encoded);\n content.push(` ${safeName} as ${JSON.stringify(keyword)},`);\n }\n content.push('};');\n content.push('');\n\n return content.join('\\n');\n};\n","/**\n * @license\n * SPDX-License-Identifier: MIT\n */\n\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { globby } from 'globby';\nimport pLimit from 'p-limit';\nimport { extractKeywords, type KeywordSet } from './transform.js';\nimport { generateTypeDeclaration } from './typegen.js';\n\nconst collectKeywordsFromRoot = async (\n root: string,\n silent: boolean,\n ignoredDirs: string[] = [],\n concurrency: number = 100,\n): Promise<KeywordSet> => {\n const collectedKeywords: KeywordSet = { main: new Set(), local: new Set() };\n\n const start = performance.now();\n if (!silent) {\n console.error('Scanning project files for keywords...');\n }\n\n const files = await globby('**/*.{js,ts,mjs,mts,jsx,tsx,mjsx,mtsx}', {\n cwd: root,\n absolute: false,\n ignore: ['**/node_modules/**', ...ignoredDirs.map((dir) => `${dir}/**`)],\n gitignore: true,\n });\n\n let processed = 0;\n const limit = pLimit({ concurrency });\n await limit.map(files, async (file) => {\n try {\n const code = await readFile(file, 'utf-8');\n const keywords = extractKeywords(code);\n if (!keywords) {\n return;\n }\n for (const keyword of keywords.main) {\n collectedKeywords.main.add(keyword);\n }\n for (const keyword of keywords.local) {\n collectedKeywords.local.add(keyword);\n }\n processed++;\n } catch (error) {\n if (!silent) {\n console.error(`Failed to process ${file}: ${error}`);\n }\n }\n });\n\n const elapsed = performance.now() - start;\n if (!silent) {\n console.error(\n `Scan complete: ${processed}/${files.length} files, ${collectedKeywords.main.size} main, ${collectedKeywords.local.size} local keywords (${elapsed.toFixed(2)}ms).`,\n );\n }\n\n return collectedKeywords;\n};\n\ninterface RunnerOptions {\n root: string;\n silent: boolean;\n outDir: string;\n}\n\nexport const createRunner = (options?: Partial<RunnerOptions>) => {\n const {\n root = process.cwd(),\n silent = false,\n outDir = path.join('node_modules', '.keywords'),\n } = options ?? {};\n return {\n async collect(): Promise<KeywordSet> {\n return collectKeywordsFromRoot(root, silent);\n },\n\n async save(keywords: KeywordSet): Promise<void> {\n const content = generateTypeDeclaration(keywords.main);\n const localContent = generateTypeDeclaration(keywords.local, true);\n const outPath = path.join(root, outDir);\n await mkdir(outPath, { recursive: true });\n await writeFile(path.join(outPath, 'index.d.ts'), `${content.trim()}\\n`);\n await writeFile(\n path.join(outPath, 'local.d.ts'),\n `${localContent.trim()}\\n`,\n );\n },\n\n async run(): Promise<void> {\n const keywords = await this.collect();\n await this.save(keywords);\n },\n };\n};\n","/**\n * @license\n * SPDX-License-Identifier: MIT\n */\n\nimport { createHmac, hkdfSync } from 'node:crypto';\nimport {\n HASH_LENGTH,\n VIRTUAL_LOCAL_MODULE_ID,\n VIRTUAL_MODULE_ID,\n} from './constants.js';\n\nconst ALPHA_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';\nconst DIGIT_CHARS = '0123456789';\nconst BASE62_CHARS = ALPHA_CHARS + DIGIT_CHARS;\n\nconst ALPHA_LEN = BigInt(ALPHA_CHARS.length); // 52n\nconst DIGIT_LEN = BigInt(DIGIT_CHARS.length); // 10n\nconst BASE62_LEN = BigInt(BASE62_CHARS.length); // 62n\n\nexport type Hasher = (input: string) => string;\n\n// Format: [1 Alpha] + [1 Digit] + [N Base62]\n// Avoids any collisions with standard JS API identifiers\nexport const createHasher = (secret: string): Hasher => {\n const base62TailLength = HASH_LENGTH - 2;\n if (base62TailLength < 0 || base62TailLength > 9) {\n // 520 * 62^9 < 2^64 < 520 * 62^10\n throw new Error('Invalid MAX_HASH_LENGTH');\n }\n\n const cache = new Map<string, string>();\n return (input) => {\n if (cache.has(input)) {\n return cache.get(input) as string;\n }\n\n const info = VIRTUAL_MODULE_ID;\n const payload = `${info.length}:${info}|${input.length}:${input}`;\n const hasher = createHmac('sha256', secret);\n const buffer = hasher.update(payload).digest('hex');\n\n let entropy = BigInt(`0x${buffer}`);\n let result = '';\n\n result += ALPHA_CHARS[Number(entropy % ALPHA_LEN)];\n entropy /= ALPHA_LEN;\n result += DIGIT_CHARS[Number(entropy % DIGIT_LEN)];\n entropy /= DIGIT_LEN;\n for (let i = 0; i < base62TailLength; i++) {\n result += BASE62_CHARS[Number(entropy % BASE62_LEN)];\n entropy /= BASE62_LEN;\n }\n\n cache.set(input, result);\n return result;\n };\n};\n\n// Fisher-Yates based deterministic shuffle\nconst shuffle = (str: string, secret: string, salt: string): string => {\n const arr = Array.from(str);\n const requiredBytes = (arr.length - 1) * 4;\n\n const info = VIRTUAL_LOCAL_MODULE_ID;\n const keyingMaterial = hkdfSync('sha256', secret, salt, info, requiredBytes);\n const prngBuffer = Buffer.from(keyingMaterial);\n\n let byteOffset = 0;\n for (let i = arr.length - 1; i > 0; i--) {\n const random32 = prngBuffer.readUInt32BE(byteOffset);\n byteOffset += 4;\n const j = random32 % (i + 1);\n const temp = arr[i] as string;\n arr[i] = arr[j] as string;\n arr[j] = temp;\n }\n\n return arr.join('');\n};\n\nexport const createCounter = (secret: string): Hasher => {\n const shuffledAlpha = shuffle(ALPHA_CHARS, secret, 'alpha');\n const shuffledDigit = shuffle(DIGIT_CHARS, secret, 'digit');\n const shuffledBase62 = shuffle(BASE62_CHARS, secret, 'base62');\n\n let index = 0;\n const cache = new Map<string, string>();\n return (input) => {\n if (cache.has(input)) {\n return cache.get(input) as string;\n }\n\n let result = '';\n let current = index;\n index++;\n\n result += shuffledAlpha[current % shuffledAlpha.length];\n current = Math.floor(current / shuffledAlpha.length);\n result += shuffledDigit[current % shuffledDigit.length];\n current = Math.floor(current / shuffledDigit.length);\n while (current > 0) {\n current--;\n result += shuffledBase62[current % shuffledBase62.length];\n current = Math.floor(current / shuffledBase62.length);\n }\n\n cache.set(input, result);\n return result;\n };\n};\n"],"mappings":"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"}
|