@elliots/unplugin-typical 0.1.10 → 0.2.0

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.
@@ -1,4 +1,4 @@
1
- import { t as Typical } from "./index-DDxe82RX.mjs";
1
+ import { t as Typical } from "./index-DrHjl95P.mjs";
2
2
 
3
3
  //#region src/esbuild.d.ts
4
4
 
package/dist/esbuild.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as Typical } from "./src-D0Juym-h.mjs";
1
+ import { t as Typical } from "./src-DKwWaQcb.mjs";
2
2
 
3
3
  //#region src/esbuild.ts
4
4
  /**
package/dist/farm.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as Typical } from "./index-DDxe82RX.mjs";
1
+ import { t as Typical } from "./index-DrHjl95P.mjs";
2
2
 
3
3
  //#region src/farm.d.ts
4
4
 
package/dist/farm.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as Typical } from "./src-D0Juym-h.mjs";
1
+ import { t as Typical } from "./src-DKwWaQcb.mjs";
2
2
 
3
3
  //#region src/farm.ts
4
4
  /**
@@ -1,5 +1,4 @@
1
1
  import { FilterPattern, UnpluginInstance } from "unplugin";
2
- import ts from "typescript";
3
2
 
4
3
  //#region ../../dist/src/config.d.ts
5
4
  interface TypicalDebugConfig {
@@ -47,6 +46,18 @@ interface TypicalConfig {
47
46
  * Default: true
48
47
  */
49
48
  validateFunctions?: boolean;
49
+ /**
50
+ * Transform JSON.parse<T>() calls to validate and filter the parsed result
51
+ * to only include properties defined in type T.
52
+ * Default: true
53
+ */
54
+ transformJSONParse?: boolean;
55
+ /**
56
+ * Transform JSON.stringify<T>() calls to only stringify properties defined
57
+ * in type T, preventing accidental data leaks.
58
+ * Default: true
59
+ */
60
+ transformJSONStringify?: boolean;
50
61
  /**
51
62
  * Source map generation settings.
52
63
  * Controls whether and how source maps are generated for transformed code.
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { n as Options, t as Typical } from "./index-DDxe82RX.mjs";
1
+ import { n as Options, t as Typical } from "./index-DrHjl95P.mjs";
2
2
  export { Options, Typical };
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { t as Typical } from "./src-D0Juym-h.mjs";
1
+ import { t as Typical } from "./src-DKwWaQcb.mjs";
2
2
 
3
3
  export { Typical };
@@ -1,4 +1,4 @@
1
- import { t as Typical } from "./index-DDxe82RX.mjs";
1
+ import { t as Typical } from "./index-DrHjl95P.mjs";
2
2
 
3
3
  //#region src/rolldown.d.ts
4
4
 
package/dist/rolldown.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as Typical } from "./src-D0Juym-h.mjs";
1
+ import { t as Typical } from "./src-DKwWaQcb.mjs";
2
2
 
3
3
  //#region src/rolldown.ts
4
4
  /**
package/dist/rollup.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as Typical } from "./index-DDxe82RX.mjs";
1
+ import { t as Typical } from "./index-DrHjl95P.mjs";
2
2
 
3
3
  //#region src/rollup.d.ts
4
4
 
package/dist/rollup.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as Typical } from "./src-D0Juym-h.mjs";
1
+ import { t as Typical } from "./src-DKwWaQcb.mjs";
2
2
 
3
3
  //#region src/rollup.ts
4
4
  /**
package/dist/rspack.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as Typical } from "./index-DDxe82RX.mjs";
1
+ import { t as Typical } from "./index-DrHjl95P.mjs";
2
2
 
3
3
  //#region src/rspack.d.ts
4
4
 
package/dist/rspack.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as Typical } from "./src-D0Juym-h.mjs";
1
+ import { t as Typical } from "./src-DKwWaQcb.mjs";
2
2
 
3
3
  //#region src/rspack.ts
4
4
  /**
@@ -0,0 +1,95 @@
1
+ import { createUnplugin } from "unplugin";
2
+ import { TypicalTransformer, buildTimer, loadConfig } from "@elliots/typical";
3
+ import { extname, resolve } from "path";
4
+
5
+ //#region src/core/options.ts
6
+ function resolveOptions(options) {
7
+ return {
8
+ include: options.include || [/\.[cm]?[jt]sx?$/],
9
+ exclude: options.exclude || [/node_modules/],
10
+ enforce: "enforce" in options ? options.enforce : "pre",
11
+ typical: options.typical
12
+ };
13
+ }
14
+
15
+ //#endregion
16
+ //#region src/core/transform.ts
17
+ const TRANSFORM_EXTENSIONS = new Set([
18
+ ".ts",
19
+ ".tsx",
20
+ ".mts",
21
+ ".cts"
22
+ ]);
23
+ let transformer = null;
24
+ /**
25
+ * Transform a TypeScript file with Typical.
26
+ *
27
+ * Uses the Go compiler via TypicalCompiler for validation code generation.
28
+ * The Go server stays running and maintains the TypeScript program state.
29
+ */
30
+ async function transformTypia(id, _source, config) {
31
+ buildTimer.start("total-transform");
32
+ const ext = extname(id).toLowerCase();
33
+ if (!TRANSFORM_EXTENSIONS.has(ext)) {
34
+ buildTimer.end("total-transform");
35
+ return;
36
+ }
37
+ const resolvedId = resolve(id);
38
+ if (!transformer) {
39
+ buildTimer.start("init-transformer");
40
+ transformer = new TypicalTransformer(config);
41
+ buildTimer.end("init-transformer");
42
+ }
43
+ buildTimer.start("transform");
44
+ const result = await transformer.transform(resolvedId, "ts");
45
+ buildTimer.end("transform");
46
+ buildTimer.end("total-transform");
47
+ if (process.env.DEBUG) console.log("[unplugin-typical] Transform output (first 1000 chars):", result.code.substring(0, 1e3));
48
+ return {
49
+ code: result.code,
50
+ map: result.map
51
+ };
52
+ }
53
+ /**
54
+ * Close the transformer and release resources.
55
+ * Should be called at build end.
56
+ */
57
+ async function closeTransformer() {
58
+ if (transformer) {
59
+ await transformer.close();
60
+ transformer = null;
61
+ }
62
+ }
63
+
64
+ //#endregion
65
+ //#region src/index.ts
66
+ const Typical = createUnplugin((rawOptions = {}) => {
67
+ const options = resolveOptions(rawOptions);
68
+ const typicalConfig = {
69
+ ...loadConfig(),
70
+ ...options.typical
71
+ };
72
+ return {
73
+ name: "unplugin-typical",
74
+ enforce: options.enforce,
75
+ buildStart() {
76
+ buildTimer.reset();
77
+ },
78
+ async buildEnd() {
79
+ if (process.env.DEBUG) buildTimer.report();
80
+ await closeTransformer();
81
+ },
82
+ transform: {
83
+ filter: { id: {
84
+ include: options.include,
85
+ exclude: options.exclude
86
+ } },
87
+ handler(code, id) {
88
+ return transformTypia(id, code, typicalConfig);
89
+ }
90
+ }
91
+ };
92
+ });
93
+
94
+ //#endregion
95
+ export { Typical as t };
package/dist/vite.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as Typical } from "./index-DDxe82RX.mjs";
1
+ import { t as Typical } from "./index-DrHjl95P.mjs";
2
2
 
3
3
  //#region src/vite.d.ts
4
4
 
package/dist/vite.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as Typical } from "./src-D0Juym-h.mjs";
1
+ import { t as Typical } from "./src-DKwWaQcb.mjs";
2
2
 
3
3
  //#region src/vite.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as Typical } from "./index-DDxe82RX.mjs";
1
+ import { t as Typical } from "./index-DrHjl95P.mjs";
2
2
 
3
3
  //#region src/webpack.d.ts
4
4
 
package/dist/webpack.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as Typical } from "./src-D0Juym-h.mjs";
1
+ import { t as Typical } from "./src-DKwWaQcb.mjs";
2
2
 
3
3
  //#region src/webpack.ts
4
4
  /**
package/package.json CHANGED
@@ -1,44 +1,36 @@
1
1
  {
2
2
  "name": "@elliots/unplugin-typical",
3
- "type": "module",
4
- "version": "0.1.10",
3
+ "version": "0.2.0",
5
4
  "description": "Unplugin for typical - runtime safe TypeScript transformer",
6
- "author": "Elliot Shepherd <elliot@jarofworms.com>",
7
- "license": "MIT",
8
- "homepage": "https://github.com/elliots/typical#readme",
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/elliots/typical.git",
12
- "directory": "packages/unplugin"
13
- },
14
- "bugs": {
15
- "url": "https://github.com/elliots/typical/issues"
16
- },
17
5
  "keywords": [
18
- "unplugin",
19
- "vite",
20
- "webpack",
21
- "rspack",
22
- "rollup",
23
- "rolldown",
24
6
  "esbuild",
25
7
  "farm",
26
- "typescript",
8
+ "rolldown",
9
+ "rollup",
10
+ "rspack",
27
11
  "runtime",
12
+ "typescript",
13
+ "typical",
14
+ "unplugin",
28
15
  "validation",
29
- "typical"
16
+ "vite",
17
+ "webpack"
30
18
  ],
31
- "exports": {
32
- ".": "./dist/index.mjs",
33
- "./esbuild": "./dist/esbuild.mjs",
34
- "./farm": "./dist/farm.mjs",
35
- "./rolldown": "./dist/rolldown.mjs",
36
- "./rollup": "./dist/rollup.mjs",
37
- "./rspack": "./dist/rspack.mjs",
38
- "./vite": "./dist/vite.mjs",
39
- "./webpack": "./dist/webpack.mjs",
40
- "./package.json": "./package.json"
19
+ "homepage": "https://github.com/elliots/typical#readme",
20
+ "bugs": {
21
+ "url": "https://github.com/elliots/typical/issues"
22
+ },
23
+ "license": "MIT",
24
+ "author": "Elliot Shepherd <elliot@jarofworms.com>",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/elliots/typical.git",
28
+ "directory": "packages/unplugin"
41
29
  },
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "type": "module",
42
34
  "main": "./dist/index.mjs",
43
35
  "module": "./dist/index.mjs",
44
36
  "types": "./dist/index.d.mts",
@@ -50,14 +42,22 @@
50
42
  ]
51
43
  }
52
44
  },
53
- "files": [
54
- "dist"
55
- ],
45
+ "exports": {
46
+ ".": "./dist/index.mjs",
47
+ "./esbuild": "./dist/esbuild.mjs",
48
+ "./farm": "./dist/farm.mjs",
49
+ "./rolldown": "./dist/rolldown.mjs",
50
+ "./rollup": "./dist/rollup.mjs",
51
+ "./rspack": "./dist/rspack.mjs",
52
+ "./vite": "./dist/vite.mjs",
53
+ "./webpack": "./dist/webpack.mjs",
54
+ "./package.json": "./package.json"
55
+ },
56
56
  "publishConfig": {
57
57
  "access": "public"
58
58
  },
59
59
  "dependencies": {
60
- "@elliots/typical": "0.1.10",
60
+ "@elliots/typical": "0.2.0",
61
61
  "unplugin": "^2.3.11"
62
62
  },
63
63
  "devDependencies": {
@@ -1,255 +0,0 @@
1
- import { createUnplugin } from "unplugin";
2
- import { TypicalTransformer, loadConfig, validateConfig } from "@elliots/typical";
3
- import { dirname, extname, resolve } from "path";
4
- import ts from "typescript";
5
-
6
- //#region src/core/options.ts
7
- function resolveOptions(options) {
8
- return {
9
- include: options.include || [/\.[cm]?[jt]sx?$/],
10
- exclude: options.exclude || [/node_modules/],
11
- enforce: "enforce" in options ? options.enforce : "pre",
12
- typical: options.typical
13
- };
14
- }
15
-
16
- //#endregion
17
- //#region src/core/timing.ts
18
- /**
19
- * Performance instrumentation for tracking build times
20
- */
21
- var BuildTimer = class {
22
- timings = /* @__PURE__ */ new Map();
23
- starts = /* @__PURE__ */ new Map();
24
- start(stage) {
25
- this.starts.set(stage, performance.now());
26
- }
27
- end(stage) {
28
- const start = this.starts.get(stage);
29
- if (start !== void 0) {
30
- const duration = performance.now() - start;
31
- const existing = this.timings.get(stage) ?? [];
32
- existing.push(duration);
33
- this.timings.set(stage, existing);
34
- this.starts.delete(stage);
35
- }
36
- }
37
- reset() {
38
- this.timings.clear();
39
- this.starts.clear();
40
- }
41
- report() {
42
- console.log("\n[unplugin-typical] Build Performance Report:");
43
- console.log("─".repeat(60));
44
- const sortedStages = Array.from(this.timings.entries()).sort(([, a], [, b]) => b.reduce((x, y) => x + y, 0) - a.reduce((x, y) => x + y, 0));
45
- let totalTime = 0;
46
- for (const [stage, times] of sortedStages) {
47
- const total = times.reduce((a, b) => a + b, 0);
48
- const avg = total / times.length;
49
- const min = Math.min(...times);
50
- const max = Math.max(...times);
51
- const count = times.length;
52
- totalTime += total;
53
- console.log(`${stage}:`);
54
- console.log(` Count: ${count}`);
55
- console.log(` Total: ${total.toFixed(2)}ms`);
56
- console.log(` Avg: ${avg.toFixed(2)}ms`);
57
- console.log(` Min: ${min.toFixed(2)}ms`);
58
- console.log(` Max: ${max.toFixed(2)}ms`);
59
- }
60
- console.log("─".repeat(60));
61
- console.log(`Total transform time: ${totalTime.toFixed(2)}ms`);
62
- console.log("");
63
- }
64
- getTimings() {
65
- const result = /* @__PURE__ */ new Map();
66
- for (const [stage, times] of this.timings) {
67
- const total = times.reduce((a, b) => a + b, 0);
68
- result.set(stage, {
69
- count: times.length,
70
- total,
71
- avg: total / times.length,
72
- min: Math.min(...times),
73
- max: Math.max(...times)
74
- });
75
- }
76
- return result;
77
- }
78
- };
79
- const buildTimer = new BuildTimer();
80
-
81
- //#endregion
82
- //#region src/core/transform.ts
83
- const TRANSFORM_EXTENSIONS = new Set([
84
- ".ts",
85
- ".tsx",
86
- ".mts",
87
- ".cts"
88
- ]);
89
- /**
90
- * Transform a TypeScript file with Typical.
91
- *
92
- * Uses a shared ProgramManager for incremental compilation across files.
93
- * Returns both code and source map for use with Vite/Rollup/Webpack.
94
- */
95
- function transformTypia(id, source, config, programManager, options = {}) {
96
- buildTimer.start("total-transform");
97
- const ext = extname(id).toLowerCase();
98
- if (!TRANSFORM_EXTENSIONS.has(ext)) {
99
- buildTimer.end("total-transform");
100
- return;
101
- }
102
- const resolvedId = resolve(id);
103
- buildTimer.start("get-program");
104
- const program = programManager.getProgram(resolvedId, source);
105
- buildTimer.end("get-program");
106
- buildTimer.start("get-source-file");
107
- const sourceFile = programManager.getSourceFile(resolvedId);
108
- buildTimer.end("get-source-file");
109
- if (!sourceFile) {
110
- buildTimer.end("total-transform");
111
- console.warn(`[unplugin-typical] Could not get source file for: ${id}`);
112
- return;
113
- }
114
- buildTimer.start("create-transformer");
115
- const transformer = new TypicalTransformer(validateConfig(config), program);
116
- buildTimer.end("create-transformer");
117
- const generateSourceMap = options.sourceMap ?? config.sourceMap?.enabled ?? true;
118
- buildTimer.start("transform");
119
- const result = transformer.transform(sourceFile, "js", { sourceMap: generateSourceMap });
120
- buildTimer.end("transform");
121
- buildTimer.end("total-transform");
122
- if (process.env.DEBUG) console.log("[unplugin-typical] Transform output (first 1000 chars):", result.code.substring(0, 1e3));
123
- return {
124
- code: result.code,
125
- map: result.map
126
- };
127
- }
128
-
129
- //#endregion
130
- //#region src/core/program-manager.ts
131
- /**
132
- * Manages a shared TypeScript program across file transformations.
133
- * This avoids the expensive cost of creating a new program for each file.
134
- */
135
- var ProgramManager = class {
136
- program;
137
- compilerOptions;
138
- sourceContents = /* @__PURE__ */ new Map();
139
- sourceFileCache = /* @__PURE__ */ new Map();
140
- host;
141
- /**
142
- * Get or create a program with the given source content for a file.
143
- * Uses incremental compilation to reuse data from previous program.
144
- */
145
- getProgram(id, source) {
146
- const resolvedId = resolve(id);
147
- this.sourceContents.set(resolvedId, source);
148
- this.sourceFileCache.delete(resolvedId);
149
- if (!this.compilerOptions) {
150
- buildTimer.start("load-compiler-options");
151
- this.compilerOptions = this.loadCompilerOptions();
152
- buildTimer.end("load-compiler-options");
153
- }
154
- if (!this.host) this.host = this.createHost();
155
- const rootFiles = this.program?.getRootFileNames() ?? [];
156
- const rootFileSet = new Set(rootFiles);
157
- if (!rootFileSet.has(resolvedId)) rootFileSet.add(resolvedId);
158
- buildTimer.start("create-program-incremental");
159
- this.program = ts.createProgram(Array.from(rootFileSet), this.compilerOptions, this.host, this.program);
160
- buildTimer.end("create-program-incremental");
161
- return this.program;
162
- }
163
- /**
164
- * Get the source file for a given ID from the current program.
165
- */
166
- getSourceFile(id) {
167
- const resolvedId = resolve(id);
168
- return this.program?.getSourceFile(resolvedId);
169
- }
170
- /**
171
- * Reset the program manager state (e.g., at build start).
172
- */
173
- reset() {
174
- this.program = void 0;
175
- this.sourceContents.clear();
176
- }
177
- loadCompilerOptions() {
178
- const configPath = ts.findConfigFile(process.cwd(), ts.sys.fileExists, "tsconfig.json");
179
- if (!configPath) return {
180
- target: ts.ScriptTarget.ES2020,
181
- module: ts.ModuleKind.ESNext,
182
- moduleResolution: ts.ModuleResolutionKind.Bundler,
183
- esModuleInterop: true,
184
- strict: true
185
- };
186
- const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
187
- if (configFile.error) throw new Error(ts.flattenDiagnosticMessageText(configFile.error.messageText, "\n"));
188
- return ts.parseJsonConfigFileContent(configFile.config, ts.sys, dirname(configPath)).options;
189
- }
190
- createHost() {
191
- const baseHost = ts.createCompilerHost(this.compilerOptions);
192
- const originalGetSourceFile = baseHost.getSourceFile.bind(baseHost);
193
- return {
194
- ...baseHost,
195
- getSourceFile: (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
196
- const resolvedFileName = resolve(fileName);
197
- const virtualContent = this.sourceContents.get(resolvedFileName);
198
- if (virtualContent !== void 0) {
199
- const cached = this.sourceFileCache.get(resolvedFileName);
200
- if (cached && cached.text === virtualContent) return cached;
201
- const sourceFile = ts.createSourceFile(resolvedFileName, virtualContent, languageVersion, true);
202
- this.sourceFileCache.set(resolvedFileName, sourceFile);
203
- return sourceFile;
204
- }
205
- const cachedDisk = this.sourceFileCache.get(resolvedFileName);
206
- if (cachedDisk) return cachedDisk;
207
- const result = originalGetSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
208
- if (result) this.sourceFileCache.set(resolvedFileName, result);
209
- return result;
210
- },
211
- fileExists: (fileName) => {
212
- const resolvedFileName = resolve(fileName);
213
- return this.sourceContents.has(resolvedFileName) || baseHost.fileExists(fileName);
214
- },
215
- readFile: (fileName) => {
216
- const resolvedFileName = resolve(fileName);
217
- return this.sourceContents.get(resolvedFileName) ?? baseHost.readFile(fileName);
218
- }
219
- };
220
- }
221
- };
222
-
223
- //#endregion
224
- //#region src/index.ts
225
- const Typical = createUnplugin((rawOptions = {}) => {
226
- const options = resolveOptions(rawOptions);
227
- const typicalConfig = {
228
- ...loadConfig(),
229
- ...options.typical
230
- };
231
- const programManager = new ProgramManager();
232
- return {
233
- name: "unplugin-typical",
234
- enforce: options.enforce,
235
- buildStart() {
236
- buildTimer.reset();
237
- programManager.reset();
238
- },
239
- buildEnd() {
240
- if (process.env.DEBUG) buildTimer.report();
241
- },
242
- transform: {
243
- filter: { id: {
244
- include: options.include,
245
- exclude: options.exclude
246
- } },
247
- handler(code, id) {
248
- return transformTypia(id, code, typicalConfig, programManager);
249
- }
250
- }
251
- };
252
- });
253
-
254
- //#endregion
255
- export { Typical as t };