@elliots/typical 0.1.9 → 0.2.0-beta.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 +232 -96
- package/dist/src/cli.js +14 -60
- package/dist/src/cli.js.map +1 -1
- package/dist/src/cli.typical.ts +136 -0
- package/dist/src/config.d.ts +56 -0
- package/dist/src/config.js +124 -32
- package/dist/src/config.js.map +1 -1
- package/dist/src/config.typical.ts +287 -0
- package/dist/src/esm-loader-register.js.map +1 -1
- package/dist/src/esm-loader.d.ts +1 -0
- package/dist/src/esm-loader.js +31 -8
- package/dist/src/esm-loader.js.map +1 -1
- package/dist/src/file-filter.d.ts +1 -1
- package/dist/src/file-filter.js.map +1 -1
- package/dist/src/index.d.ts +4 -1
- package/dist/src/index.js +2 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/program-manager.d.ts +27 -0
- package/dist/src/program-manager.js +121 -0
- package/dist/src/program-manager.js.map +1 -0
- package/dist/src/regex-hoister.d.ts +1 -1
- package/dist/src/regex-hoister.js +13 -19
- package/dist/src/regex-hoister.js.map +1 -1
- package/dist/src/setup.d.ts +1 -1
- package/dist/src/setup.js +3 -3
- package/dist/src/setup.js.map +1 -1
- package/dist/src/source-map.d.ts +78 -0
- package/dist/src/source-map.js +133 -0
- package/dist/src/source-map.js.map +1 -0
- package/dist/src/source-map.typical.ts +216 -0
- package/dist/src/timing.d.ts +19 -0
- package/dist/src/timing.js +65 -0
- package/dist/src/timing.js.map +1 -0
- package/dist/src/transformer.d.ts +28 -126
- package/dist/src/transformer.js +44 -1477
- package/dist/src/transformer.js.map +1 -1
- package/dist/src/transformer.typical.ts +2552 -0
- package/dist/src/tsc-plugin.d.ts +8 -1
- package/dist/src/tsc-plugin.js +11 -7
- package/dist/src/tsc-plugin.js.map +1 -1
- package/package.json +54 -44
- package/src/cli.ts +45 -98
- package/src/config.ts +200 -57
- package/src/esm-loader-register.ts +2 -2
- package/src/esm-loader.ts +46 -19
- package/src/index.ts +5 -2
- package/src/patch-fs.cjs +14 -14
- package/src/timing.ts +74 -0
- package/src/transformer.ts +52 -1969
- package/bin/ttsc +0 -12
- package/src/file-filter.ts +0 -49
- package/src/patch-tsconfig.cjs +0 -52
- package/src/regex-hoister.ts +0 -203
- package/src/setup.ts +0 -39
- package/src/tsc-plugin.ts +0 -12
package/README.md
CHANGED
|
@@ -1,89 +1,129 @@
|
|
|
1
1
|
# Typical
|
|
2
2
|
|
|
3
|
-
Typical
|
|
3
|
+
Typical makes TypeScript type-safe at runtime _with no changes to your code_.
|
|
4
4
|
|
|
5
|
-
It
|
|
5
|
+
It transforms your code to inject runtime validation based on your existing type annotations. With source maps, so errors point to the right lines in your original code.
|
|
6
6
|
|
|
7
7
|
## Why?
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- Less need for zod, yup, ajv, or other runtime validation libraries - your types are already validated automatically. If you can express it in TypeScript, Typical can validate it at runtime.
|
|
10
|
+
- Protects against data leaks via `JSON.stringify` by ensuring only properties defined in your types are included
|
|
11
|
+
- Catches type mismatches at runtime that TypeScript can't catch at compile time (API responses, JSON parsing, un-typed/badly-typed libraries, vibe-coding coworkers etc.)
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
## Features
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
- Validation of function parameters and return types
|
|
16
|
+
- Safe `JSON.parse` with type validation
|
|
17
|
+
- Safe `JSON.stringify` that only includes defined properties
|
|
18
|
+
- Validation of type casts (`as Type`)
|
|
19
|
+
- Configurable include/exclude patterns
|
|
14
20
|
|
|
15
|
-
##
|
|
21
|
+
## Example
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
- ✅ Automatic validation of return types
|
|
19
|
-
- ✅ Replace `JSON.stringify` with a custom stringifier (very fast!)
|
|
20
|
-
- ✅ Replace `JSON.parse` with a custom parser and validator (very fast!)
|
|
21
|
-
- ✅ Configurable include/exclude patterns
|
|
22
|
-
- ✅ Optionally reuse validation logic for identical types to optimize performance (enabled by default)
|
|
23
|
-
- ✅ TSC plugin
|
|
24
|
-
- ✅ ESM loader for runtime transformation with `node --import @elliots/typical/esm` (or `node --loader @elliots/typical/esm-loader` for older Node versions)
|
|
25
|
-
- ✅ tsx wrapper (ttsx) for easy use like `npx ttsx script.ts`
|
|
26
|
-
- ✅ Unplugin for Vite, Webpack, Rollup, esbuild, and more
|
|
23
|
+
This code runs without errors in normal TypeScript, but Typical catches the invalid data:
|
|
27
24
|
|
|
28
|
-
|
|
25
|
+
```ts
|
|
26
|
+
interface User {
|
|
27
|
+
name: string;
|
|
28
|
+
email: `${string}@${string}`;
|
|
29
|
+
}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
// This will throw - email doesn't match the template literal type
|
|
32
|
+
const user = JSON.parse('{"name":"Alice","email":"not-an-email"}') as User;
|
|
32
33
|
```
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Usage Options
|
|
38
|
+
|
|
39
|
+
Choose the integration that fits your workflow:
|
|
35
40
|
|
|
36
|
-
|
|
41
|
+
| Method | Best For | Package |
|
|
42
|
+
| --------------------------------------------------------- | ------------------------------- | ----------------------------- |
|
|
43
|
+
| [ESM Loader](#nodejs-esm-loader) | Node.js scripts, development | `@elliots/typical` |
|
|
44
|
+
| [ttsx](#ttsx-tsx-wrapper) | Quick scripts with tsx | `@elliots/typical` + `tsx` |
|
|
45
|
+
| [Bun Plugin](#bun) | Bun projects | `@elliots/bun-plugin-typical` |
|
|
46
|
+
| [Vite/Webpack/etc](#bundlers-vite-webpack-rollup-esbuild) | Frontend apps, bundled projects | `@elliots/unplugin-typical` |
|
|
47
|
+
| [tsc Plugin](#typescript-compiler-tsc) | Pure TypeScript compilation | `@elliots/typical-tsc-plugin` |
|
|
37
48
|
|
|
38
|
-
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Node.js (ESM Loader)
|
|
52
|
+
|
|
53
|
+
The simplest way to run TypeScript with Typical validation.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm add @elliots/typical
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
node --import @elliots/typical/esm src/index.ts
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Add to `package.json` scripts:
|
|
39
64
|
|
|
40
65
|
```json
|
|
41
66
|
{
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"validateFunctions": true,
|
|
46
|
-
"validateCasts": false,
|
|
47
|
-
"hoistRegex": true,
|
|
48
|
-
"ignoreDOMTypes": true,
|
|
49
|
-
"ignoreTypes": []
|
|
67
|
+
"scripts": {
|
|
68
|
+
"start": "node --import @elliots/typical/esm src/index.ts"
|
|
69
|
+
}
|
|
50
70
|
}
|
|
51
71
|
```
|
|
52
72
|
|
|
53
|
-
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## ttsx (tsx wrapper)
|
|
76
|
+
|
|
77
|
+
A convenience wrapper that combines [tsx](https://github.com/privatenumber/tsx) with Typical.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm add @elliots/typical tsx
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npx ttsx script.ts
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Or install globally:
|
|
54
88
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
| `reusableValidators` | `true` | Create shared validators for identical types (smaller output, allows reuse) |
|
|
60
|
-
| `validateFunctions` | `true` | Validate function parameters and return types at runtime |
|
|
61
|
-
| `validateCasts` | `false` | Validate type assertions (`as Type`) at runtime |
|
|
62
|
-
| `hoistRegex` | `true` | Hoist regex patterns to top-level constants (improves performance) |
|
|
63
|
-
| `ignoreDOMTypes` | `true` | Skip validation for DOM types (Document, Element, etc.) |
|
|
64
|
-
| `ignoreTypes` | `[]` | Type patterns to skip validation for (supports wildcards, e.g., `["React.*"]`) |
|
|
89
|
+
```bash
|
|
90
|
+
npm add -g @elliots/typical tsx
|
|
91
|
+
ttsx script.ts
|
|
92
|
+
```
|
|
65
93
|
|
|
66
|
-
|
|
94
|
+
> **Note:** `tsx` must be installed separately. The `ttsx` command is a thin wrapper that runs `tsx` with the Typical ESM loader.
|
|
67
95
|
|
|
68
|
-
|
|
96
|
+
---
|
|
69
97
|
|
|
70
|
-
|
|
98
|
+
## Bun
|
|
71
99
|
|
|
72
100
|
```bash
|
|
73
|
-
|
|
74
|
-
|
|
101
|
+
bun add @elliots/bun-plugin-typical
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Create `bunfig.toml`:
|
|
105
|
+
|
|
106
|
+
```toml
|
|
107
|
+
preload = ["./preload.ts"]
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Create `preload.ts`:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
import { typicalPlugin } from "@elliots/bun-plugin-typical";
|
|
114
|
+
|
|
115
|
+
Bun.plugin(typicalPlugin());
|
|
75
116
|
```
|
|
76
117
|
|
|
77
|
-
|
|
118
|
+
Then run:
|
|
78
119
|
|
|
79
120
|
```bash
|
|
80
|
-
|
|
81
|
-
ttsx your-script.ts
|
|
121
|
+
bun run src/index.ts
|
|
82
122
|
```
|
|
83
123
|
|
|
84
|
-
|
|
124
|
+
---
|
|
85
125
|
|
|
86
|
-
|
|
126
|
+
## Bundlers (Vite, Webpack, Rollup, esbuild)
|
|
87
127
|
|
|
88
128
|
```bash
|
|
89
129
|
npm add @elliots/unplugin-typical
|
|
@@ -91,91 +131,187 @@ npm add @elliots/unplugin-typical
|
|
|
91
131
|
|
|
92
132
|
### Vite
|
|
93
133
|
|
|
94
|
-
```
|
|
134
|
+
```ts
|
|
95
135
|
// vite.config.ts
|
|
96
|
-
import Typical from
|
|
136
|
+
import Typical from "@elliots/unplugin-typical/vite";
|
|
97
137
|
|
|
98
138
|
export default defineConfig({
|
|
99
|
-
plugins: [
|
|
100
|
-
|
|
101
|
-
],
|
|
102
|
-
})
|
|
139
|
+
plugins: [Typical()],
|
|
140
|
+
});
|
|
103
141
|
```
|
|
104
142
|
|
|
105
143
|
### Webpack
|
|
106
144
|
|
|
107
|
-
```
|
|
145
|
+
```js
|
|
108
146
|
// webpack.config.js
|
|
109
|
-
const Typical = require(
|
|
147
|
+
const Typical = require("@elliots/unplugin-typical/webpack").default;
|
|
110
148
|
|
|
111
149
|
module.exports = {
|
|
112
|
-
plugins: [
|
|
113
|
-
|
|
114
|
-
],
|
|
115
|
-
}
|
|
150
|
+
plugins: [Typical()],
|
|
151
|
+
};
|
|
116
152
|
```
|
|
117
153
|
|
|
118
154
|
### Rollup
|
|
119
155
|
|
|
120
|
-
```
|
|
156
|
+
```js
|
|
121
157
|
// rollup.config.js
|
|
122
|
-
import Typical from
|
|
158
|
+
import Typical from "@elliots/unplugin-typical/rollup";
|
|
123
159
|
|
|
124
160
|
export default {
|
|
125
|
-
plugins: [
|
|
126
|
-
|
|
127
|
-
],
|
|
128
|
-
}
|
|
161
|
+
plugins: [Typical()],
|
|
162
|
+
};
|
|
129
163
|
```
|
|
130
164
|
|
|
131
165
|
### esbuild
|
|
132
166
|
|
|
133
|
-
```
|
|
134
|
-
import { build } from
|
|
135
|
-
import Typical from
|
|
167
|
+
```ts
|
|
168
|
+
import { build } from "esbuild";
|
|
169
|
+
import Typical from "@elliots/unplugin-typical/esbuild";
|
|
136
170
|
|
|
137
171
|
build({
|
|
138
172
|
plugins: [Typical()],
|
|
139
|
-
})
|
|
173
|
+
});
|
|
140
174
|
```
|
|
141
175
|
|
|
142
|
-
###
|
|
176
|
+
### Rolldown
|
|
143
177
|
|
|
144
|
-
|
|
178
|
+
```ts
|
|
179
|
+
// rolldown.config.ts
|
|
180
|
+
import Typical from "@elliots/unplugin-typical/rolldown";
|
|
145
181
|
|
|
146
|
-
|
|
147
|
-
Typical(
|
|
148
|
-
|
|
149
|
-
validateCasts: false,
|
|
150
|
-
// ... other options
|
|
151
|
-
})
|
|
182
|
+
export default {
|
|
183
|
+
plugins: [Typical()],
|
|
184
|
+
};
|
|
152
185
|
```
|
|
153
186
|
|
|
154
|
-
|
|
187
|
+
### Farm
|
|
155
188
|
|
|
156
|
-
|
|
189
|
+
```ts
|
|
190
|
+
// farm.config.ts
|
|
191
|
+
import Typical from "@elliots/unplugin-typical/farm";
|
|
157
192
|
|
|
158
|
-
|
|
193
|
+
export default {
|
|
194
|
+
plugins: [Typical()],
|
|
195
|
+
};
|
|
196
|
+
```
|
|
159
197
|
|
|
198
|
+
### Rspack
|
|
160
199
|
|
|
161
200
|
```ts
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
201
|
+
// rspack.config.ts
|
|
202
|
+
import Typical from "@elliots/unplugin-typical/rspack";
|
|
203
|
+
|
|
204
|
+
export default {
|
|
205
|
+
plugins: [Typical()],
|
|
206
|
+
};
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## TypeScript Compiler (tsc)
|
|
212
|
+
|
|
213
|
+
For projects that compile with `tsc` directly using [ts-patch](https://github.com/nonara/ts-patch).
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
npm add @elliots/typical-tsc-plugin ts-patch
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Option 1: ttsc (auto-injects plugin)
|
|
220
|
+
|
|
221
|
+
The `ttsc` command automatically injects the plugin - no config needed:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
npx ttsc
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Add to `package.json`:
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"scripts": {
|
|
232
|
+
"build": "ttsc"
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Option 2: Manual tsconfig.json
|
|
238
|
+
|
|
239
|
+
Add to your `tsconfig.json`:
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"compilerOptions": {
|
|
244
|
+
"plugins": [
|
|
245
|
+
{
|
|
246
|
+
"transform": "@elliots/typical-tsc-plugin",
|
|
247
|
+
"transformProgram": true
|
|
248
|
+
}
|
|
249
|
+
]
|
|
250
|
+
}
|
|
251
|
+
}
|
|
167
252
|
```
|
|
168
253
|
|
|
169
|
-
|
|
254
|
+
Then run ts-patch's tsc:
|
|
170
255
|
|
|
171
|
-
|
|
256
|
+
```bash
|
|
257
|
+
npx ts-patch install
|
|
258
|
+
npx tsc
|
|
259
|
+
```
|
|
172
260
|
|
|
173
|
-
|
|
261
|
+
Or add a prepare script:
|
|
174
262
|
|
|
175
|
-
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"scripts": {
|
|
266
|
+
"prepare": "ts-patch install -s",
|
|
267
|
+
"build": "tsc"
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
176
271
|
|
|
177
|
-
|
|
178
|
-
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Configuration
|
|
275
|
+
|
|
276
|
+
Create a `typical.json` file in your project root (optional):
|
|
277
|
+
|
|
278
|
+
```json
|
|
279
|
+
{
|
|
280
|
+
"include": ["**/*.ts", "**/*.tsx"],
|
|
281
|
+
"exclude": ["node_modules/**", "**/*.d.ts"],
|
|
282
|
+
"validateFunctions": true,
|
|
283
|
+
"validateCasts": false
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Options
|
|
288
|
+
|
|
289
|
+
| Option | Default | Description |
|
|
290
|
+
| ------------------- | --------------------------------------------------------- | --------------------------------------------- |
|
|
291
|
+
| `include` | `["**/*.ts", "**/*.tsx"]` | Files to transform |
|
|
292
|
+
| `exclude` | `["node_modules/**", "**/*.d.ts", "dist/**", "build/**"]` | Files to skip |
|
|
293
|
+
| `validateFunctions` | `true` | Validate function parameters and return types |
|
|
294
|
+
| `validateCasts` | `false` | Validate type assertions (`as Type`) |
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## How It Works
|
|
299
|
+
|
|
300
|
+
Typical uses a Go-based compiler that leverages the TypeScript type checker to analyze your code. It generates runtime validators that check values against their declared types.
|
|
301
|
+
|
|
302
|
+
Types that can't be validated at runtime (like generic type parameters `T`) are skipped. You can still use `any` and `unknown` to opt out of validation.
|
|
303
|
+
|
|
304
|
+
## Debugging
|
|
305
|
+
|
|
306
|
+
Set `DEBUG=1` for verbose logging:
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
DEBUG=1 npm run build
|
|
310
|
+
```
|
|
179
311
|
|
|
180
|
-
|
|
312
|
+
## Limitations
|
|
181
313
|
|
|
314
|
+
- Generic type parameters (`T`) cannot be validated - no runtime type information
|
|
315
|
+
- Type-only imports of classes aren't checked (can't do instanceof on type-only imports)
|
|
316
|
+
- Validation of functions is just not done. Need to think about that one.
|
|
317
|
+
- Some complex types may not be fully supported yet. If you find any that fail, please open an issue!
|
package/dist/src/cli.js
CHANGED
|
@@ -3,87 +3,41 @@ import { Command } from 'commander';
|
|
|
3
3
|
import * as fs from 'fs';
|
|
4
4
|
import * as path from 'path';
|
|
5
5
|
import { TypicalTransformer } from './transformer.js';
|
|
6
|
-
import { loadConfig } from './config.js';
|
|
6
|
+
import { loadConfig, validateConfig } from './config.js';
|
|
7
7
|
const program = new Command();
|
|
8
|
-
program
|
|
9
|
-
.name('typical')
|
|
10
|
-
.description('Runtime safe TypeScript transformer using typia')
|
|
11
|
-
.version('0.1.0');
|
|
8
|
+
program.name('typical').description('Runtime safe TypeScript transformer').version('0.1.0');
|
|
12
9
|
program
|
|
13
10
|
.command('transform')
|
|
14
11
|
.description('Transform a TypeScript file with runtime validation')
|
|
15
12
|
.argument('<file>', 'TypeScript file to transform')
|
|
16
13
|
.option('-o, --output <file>', 'Output file')
|
|
17
14
|
.option('-c, --config <file>', 'Config file path', 'typical.json')
|
|
18
|
-
.option('-
|
|
15
|
+
.option('-p, --project <file>', 'TypeScript config file path', 'tsconfig.json')
|
|
19
16
|
.action(async (file, options) => {
|
|
17
|
+
let transformer = null;
|
|
20
18
|
try {
|
|
21
|
-
const config = loadConfig(options.config);
|
|
22
|
-
|
|
19
|
+
const config = validateConfig(loadConfig(options.config));
|
|
20
|
+
transformer = new TypicalTransformer(config, options.project);
|
|
23
21
|
if (!fs.existsSync(file)) {
|
|
24
22
|
console.error(`File not found: ${file}`);
|
|
25
23
|
process.exit(1);
|
|
26
24
|
}
|
|
27
25
|
console.log(`Transforming ${file}...`);
|
|
28
|
-
const
|
|
29
|
-
|
|
26
|
+
const result = await transformer.transform(path.resolve(file), 'ts');
|
|
27
|
+
// Determine output file path
|
|
30
28
|
const outputFile = options.output ? path.resolve(options.output) : file + '.transformed.ts';
|
|
31
|
-
fs.writeFileSync(outputFile,
|
|
29
|
+
fs.writeFileSync(outputFile, result.code);
|
|
32
30
|
console.log(`Transformed code written to ${outputFile}`);
|
|
33
31
|
}
|
|
34
32
|
catch (error) {
|
|
35
33
|
console.error('Transformation failed:', error);
|
|
36
34
|
process.exit(1);
|
|
37
35
|
}
|
|
36
|
+
finally {
|
|
37
|
+
if (transformer) {
|
|
38
|
+
await transformer.close();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
38
41
|
});
|
|
39
|
-
// program
|
|
40
|
-
// .command('build')
|
|
41
|
-
// .description('Transform all TypeScript files in the project')
|
|
42
|
-
// .option('-c, --config <file>', 'Config file path')
|
|
43
|
-
// .option('--dry-run', 'Show what would be transformed without making changes')
|
|
44
|
-
// .action(async (options: { config?: string, dryRun?: boolean }) => {
|
|
45
|
-
// try {
|
|
46
|
-
// const transformer = new TypicalTransformer();
|
|
47
|
-
// const { glob } = await import('glob');
|
|
48
|
-
// const config = loadConfig(options.config);
|
|
49
|
-
// if (!config.include || config.include.length === 0) {
|
|
50
|
-
// console.error('No include patterns specified in config');
|
|
51
|
-
// process.exit(1);
|
|
52
|
-
// }
|
|
53
|
-
// const files: string[] = [];
|
|
54
|
-
// for (const pattern of config.include) {
|
|
55
|
-
// const matched = await glob(pattern, {
|
|
56
|
-
// ignore: config.exclude,
|
|
57
|
-
// absolute: true
|
|
58
|
-
// });
|
|
59
|
-
// files.push(...matched);
|
|
60
|
-
// }
|
|
61
|
-
// console.log(`Found ${files.length} files to transform`);
|
|
62
|
-
// if (options.dryRun) {
|
|
63
|
-
// files.forEach(file => console.log(`Would transform: ${file}`));
|
|
64
|
-
// return;
|
|
65
|
-
// }
|
|
66
|
-
// let transformed = 0;
|
|
67
|
-
// for (const file of files) {
|
|
68
|
-
// // Double-check with our shared filtering logic
|
|
69
|
-
// if (!shouldIncludeFile(file, config)) {
|
|
70
|
-
// console.log(`Skipping ${file} (excluded by filters)`);
|
|
71
|
-
// continue;
|
|
72
|
-
// }
|
|
73
|
-
// try {
|
|
74
|
-
// console.log(`Transforming ${file}...`);
|
|
75
|
-
// const transformedCode = transformer.transformFile(file, ts);
|
|
76
|
-
// fs.writeFileSync(file, transformedCode);
|
|
77
|
-
// transformed++;
|
|
78
|
-
// } catch (error) {
|
|
79
|
-
// console.error(`Failed to transform ${file}:`, error);
|
|
80
|
-
// }
|
|
81
|
-
// }
|
|
82
|
-
// console.log(`Successfully transformed ${transformed}/${files.length} files`);
|
|
83
|
-
// } catch (error) {
|
|
84
|
-
// console.error('Build failed:', error);
|
|
85
|
-
// process.exit(1);
|
|
86
|
-
// }
|
|
87
|
-
// });
|
|
88
42
|
program.parse();
|
|
89
43
|
//# sourceMappingURL=cli.js.map
|
package/dist/src/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAA;AACxB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAC5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAExD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;AAE7B,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,WAAW,CAAC,qCAAqC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;AAE3F,OAAO;KACJ,OAAO,CAAC,WAAW,CAAC;KACpB,WAAW,CAAC,qDAAqD,CAAC;KAClE,QAAQ,CAAC,QAAQ,EAAE,8BAA8B,CAAC;KAClD,MAAM,CAAC,qBAAqB,EAAE,aAAa,CAAC;KAC5C,MAAM,CAAC,qBAAqB,EAAE,kBAAkB,EAAE,cAAc,CAAC;KACjE,MAAM,CAAC,sBAAsB,EAAE,6BAA6B,EAAE,eAAe,CAAC;KAC9E,MAAM,CACL,KAAK,EACH,IAAY,EACZ,OAIC,EACD,EAAE;IACF,IAAI,WAAW,GAA8B,IAAI,CAAA;IACjD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;QACzD,WAAW,GAAG,IAAI,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;QAE7D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,KAAK,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAA;YACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,KAAK,CAAC,CAAA;QACtC,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAA;QAEpE,6BAA6B;QAC7B,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,iBAAiB,CAAA;QAE3F,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,CAAA;QACzC,OAAO,CAAC,GAAG,CAAC,+BAA+B,UAAU,EAAE,CAAC,CAAA;IAC1D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAA;QAC9C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;YAAS,CAAC;QACT,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,WAAW,CAAC,KAAK,EAAE,CAAA;QAC3B,CAAC;IACH,CAAC;AACH,CAAC,CACF,CAAA;AAEH,OAAO,CAAC,KAAK,EAAE,CAAA"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import typia from "typia";
|
|
3
|
+
//@L:3
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
//@L:4
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
//@L:5
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
//@L:6
|
|
10
|
+
import { TypicalTransformer } from './transformer.js';
|
|
11
|
+
//@L:7
|
|
12
|
+
import * as ts from 'typescript';
|
|
13
|
+
//@L:8
|
|
14
|
+
import { loadConfig, validateConfig } from './config.js';
|
|
15
|
+
//@L:9
|
|
16
|
+
import { shouldIncludeFile } from './file-filter.js';
|
|
17
|
+
//@L:10
|
|
18
|
+
import { inlineSourceMapComment, externalSourceMapComment } from './source-map.js';
|
|
19
|
+
//@L:12
|
|
20
|
+
const program = new Command();
|
|
21
|
+
//@L:14
|
|
22
|
+
program
|
|
23
|
+
.name('typical')
|
|
24
|
+
.description('Runtime safe TypeScript transformer using typia')
|
|
25
|
+
.version('0.1.0');
|
|
26
|
+
//@L:19
|
|
27
|
+
program
|
|
28
|
+
.command('transform')
|
|
29
|
+
.description('Transform a TypeScript file with runtime validation')
|
|
30
|
+
.argument('<file>', 'TypeScript file to transform')
|
|
31
|
+
.option('-o, --output <file>', 'Output file')
|
|
32
|
+
.option('-c, --config <file>', 'Config file path', 'typical.json')
|
|
33
|
+
.option('-m, --mode <mode>', 'Transformation mode: basic, typia, js', 'basic')
|
|
34
|
+
.option('--source-map', 'Generate external source map file')
|
|
35
|
+
.option('--inline-source-map', 'Include inline source map in output')
|
|
36
|
+
.option('--no-source-map', 'Disable source map generation')
|
|
37
|
+
.action(async (file: string, options: {
|
|
38
|
+
output?: string;
|
|
39
|
+
config?: string;
|
|
40
|
+
mode?: 'basic' | 'typia' | 'js';
|
|
41
|
+
sourceMap?: boolean;
|
|
42
|
+
inlineSourceMap?: boolean;
|
|
43
|
+
}) => {
|
|
44
|
+
try {
|
|
45
|
+
const config = validateConfig(loadConfig(options.config));
|
|
46
|
+
const transformer = new TypicalTransformer(config);
|
|
47
|
+
if (!fs.existsSync(file)) {
|
|
48
|
+
console.error(`File not found: ${file}`);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
// Determine source map behavior
|
|
52
|
+
const generateSourceMap = options.inlineSourceMap || options.sourceMap !== false;
|
|
53
|
+
console.log(`Transforming ${file}...`);
|
|
54
|
+
const result = transformer.transform(path.resolve(file), options.mode ?? 'basic', {
|
|
55
|
+
sourceMap: generateSourceMap,
|
|
56
|
+
});
|
|
57
|
+
// Determine output file path
|
|
58
|
+
const outputFile = options.output
|
|
59
|
+
? path.resolve(options.output)
|
|
60
|
+
: options.mode === 'js'
|
|
61
|
+
? file.replace(/\.tsx?$/, '.js')
|
|
62
|
+
: file + '.transformed.ts';
|
|
63
|
+
let outputCode = result.code;
|
|
64
|
+
// Handle source maps
|
|
65
|
+
if (result.map) {
|
|
66
|
+
if (options.inlineSourceMap) {
|
|
67
|
+
// Inline source map as data URL
|
|
68
|
+
outputCode += '\n' + inlineSourceMapComment(result.map);
|
|
69
|
+
}
|
|
70
|
+
else if (options.sourceMap !== false) {
|
|
71
|
+
// Write external source map file
|
|
72
|
+
const mapFile = outputFile + '.map';
|
|
73
|
+
fs.writeFileSync(mapFile, typia.json.stringify(result.map, null, 2));
|
|
74
|
+
outputCode += '\n' + externalSourceMapComment(path.basename(mapFile));
|
|
75
|
+
console.log(`Source map written to ${mapFile}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
fs.writeFileSync(outputFile, outputCode);
|
|
79
|
+
console.log(`Transformed code written to ${outputFile}`);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.error('Transformation failed:', error);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
// program
|
|
87
|
+
// .command('build')
|
|
88
|
+
// .description('Transform all TypeScript files in the project')
|
|
89
|
+
// .option('-c, --config <file>', 'Config file path')
|
|
90
|
+
// .option('--dry-run', 'Show what would be transformed without making changes')
|
|
91
|
+
// .action(async (options: { config?: string, dryRun?: boolean }) => {
|
|
92
|
+
// try {
|
|
93
|
+
// const transformer = new TypicalTransformer();
|
|
94
|
+
// const { glob } = await import('glob');
|
|
95
|
+
// const config = loadConfig(options.config);
|
|
96
|
+
// if (!config.include || config.include.length === 0) {
|
|
97
|
+
// console.error('No include patterns specified in config');
|
|
98
|
+
// process.exit(1);
|
|
99
|
+
// }
|
|
100
|
+
// const files: string[] = [];
|
|
101
|
+
// for (const pattern of config.include) {
|
|
102
|
+
// const matched = await glob(pattern, {
|
|
103
|
+
// ignore: config.exclude,
|
|
104
|
+
// absolute: true
|
|
105
|
+
// });
|
|
106
|
+
// files.push(...matched);
|
|
107
|
+
// }
|
|
108
|
+
// console.log(`Found ${files.length} files to transform`);
|
|
109
|
+
// if (options.dryRun) {
|
|
110
|
+
// files.forEach(file => console.log(`Would transform: ${file}`));
|
|
111
|
+
// return;
|
|
112
|
+
// }
|
|
113
|
+
// let transformed = 0;
|
|
114
|
+
// for (const file of files) {
|
|
115
|
+
// // Double-check with our shared filtering logic
|
|
116
|
+
// if (!shouldIncludeFile(file, config)) {
|
|
117
|
+
// console.log(`Skipping ${file} (excluded by filters)`);
|
|
118
|
+
// continue;
|
|
119
|
+
// }
|
|
120
|
+
// try {
|
|
121
|
+
// console.log(`Transforming ${file}...`);
|
|
122
|
+
// const transformedCode = transformer.transformFile(file, ts);
|
|
123
|
+
// fs.writeFileSync(file, transformedCode);
|
|
124
|
+
// transformed++;
|
|
125
|
+
// } catch (error) {
|
|
126
|
+
// console.error(`Failed to transform ${file}:`, error);
|
|
127
|
+
// }
|
|
128
|
+
// }
|
|
129
|
+
// console.log(`Successfully transformed ${transformed}/${files.length} files`);
|
|
130
|
+
// } catch (error) {
|
|
131
|
+
// console.error('Build failed:', error);
|
|
132
|
+
// process.exit(1);
|
|
133
|
+
// }
|
|
134
|
+
// });
|
|
135
|
+
//@L:145
|
|
136
|
+
program.parse();
|