tsnite 0.0.18 → 0.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2025
1
+ Copyright (c) 2026
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
package/README.md CHANGED
@@ -52,7 +52,7 @@ This will execute the specified TypeScript file, allowing you to quickly test an
52
52
  {
53
53
  "scripts": {
54
54
  //...
55
- "dev": "tsnite --watch --include-assets src/index.ts"
55
+ "dev": "tsnite watch --include src --exclude uploads --ext js,ts,json src/index.ts"
56
56
  }
57
57
  }
58
58
  ```
package/dist/cli.js CHANGED
@@ -1,22 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
  import { program } from 'commander';
3
- import { join } from 'node:path';
3
+ import { extname, join, resolve, relative } from 'node:path';
4
4
  import { pathToFileURL } from 'node:url';
5
5
  import { fork } from 'node:child_process';
6
6
  import { watch } from 'chokidar';
7
- import { createRequire } from 'node:module';
8
- const require = createRequire(import.meta.dirname);
9
- const { name, description, version } = require(join(import.meta.dirname, '..', 'package.json'));
7
+ import { name, description, version } from './metadata.js';
8
+ const DEFAULT_INCLUDE_PATHS = ['.'];
9
+ const DEFAULT_EXCLUDE_PATHS = ['dist', 'build', 'coverage'];
10
+ const DEFAULT_WATCH_EXTENSIONS = ['ts', 'tsx', 'js', 'jsx', 'json'];
11
+ const INTERNAL_IGNORED_PATHS = ['node_modules', '.git'];
10
12
  const pids = new Set();
11
13
  program
12
14
  .name(name)
13
15
  .description(description)
14
16
  .version(version, '-v, --version', 'Output the current version')
15
- .option('--source-root <string>', 'Source Root', '.')
16
- .option('--watch', 'Enables watch mode', false)
17
- .option('--include-assets', 'Include static files in the build', false)
18
- .argument('<string>', 'Entrypoint file')
19
- .argument('[args...]', 'Specify the Node.js command and any additional arguments')
20
17
  .showSuggestionAfterError();
21
18
  function cleanup() {
22
19
  for (const pid of pids.values()) {
@@ -24,17 +21,14 @@ function cleanup() {
24
21
  process.kill(pid, 'SIGTERM');
25
22
  }
26
23
  catch {
27
- // processo já morreu
24
+ //
28
25
  }
29
26
  }
30
27
  process.exit(0);
31
28
  }
32
29
  process.on('SIGINT', cleanup);
33
30
  process.on('SIGTERM', cleanup);
34
- async function handler() {
35
- const options = program.opts();
36
- const [entry, nodeArgs] = program.processedArgs;
37
- process.stdout.write('\x1Bc');
31
+ function spawn(entry, nodeArgs) {
38
32
  const { pid } = fork(join(process.cwd(), entry), {
39
33
  stdio: 'inherit',
40
34
  execArgv: [
@@ -46,12 +40,68 @@ async function handler() {
46
40
  ]
47
41
  });
48
42
  pids.add(pid);
49
- if (!options.watch)
43
+ }
44
+ function normalizePath(value) {
45
+ return value
46
+ .replace(/\\/g, '/')
47
+ .replace(/^\.\/+/, '')
48
+ .replace(/\/+$/, '');
49
+ }
50
+ function normalizeExt(value) {
51
+ return value.trim().replace(/^\./, '').toLowerCase();
52
+ }
53
+ function createWatchConfig(options) {
54
+ const sourceRoot = resolve(process.cwd(), options.sourceRoot);
55
+ const includePaths = options.include.map((value) => resolve(sourceRoot, value));
56
+ const excludePaths = options.exclude.map(normalizePath).filter(Boolean);
57
+ const excludeSet = new Set(excludePaths);
58
+ const allowedExts = new Set(options.ext.map(normalizeExt).filter(Boolean));
59
+ function ignored(filePath, stats) {
60
+ const rel = normalizePath(relative(sourceRoot, filePath));
61
+ // fora do sourceRoot
62
+ if (rel.startsWith('../')) {
63
+ return true;
64
+ }
65
+ // raiz do sourceRoot
66
+ if (rel === '') {
67
+ return false;
68
+ }
69
+ const segments = rel.split('/');
70
+ // ignora por nome de pasta/arquivo em qualquer nível
71
+ if (segments.some((segment) => excludeSet.has(segment))) {
72
+ return true;
73
+ }
74
+ // ignora por caminho relativo completo
75
+ if (excludePaths.some((excluded) => rel === excluded || rel.startsWith(excluded + '/'))) {
76
+ return true;
77
+ }
78
+ // nunca filtra diretório por extensão
79
+ if (stats?.isDirectory()) {
80
+ return false;
81
+ }
82
+ const extension = extname(rel).slice(1).toLowerCase();
83
+ // se tiver lista de extensões, ignora arquivo fora dela
84
+ if (allowedExts.size > 0 && !allowedExts.has(extension)) {
85
+ return true;
86
+ }
87
+ return false;
88
+ }
89
+ return {
90
+ sourceRoot,
91
+ paths: includePaths,
92
+ ignored
93
+ };
94
+ }
95
+ async function handler(entry, options, nodeArgs, isWatch) {
96
+ process.stdout.write('\x1Bc');
97
+ spawn(entry, nodeArgs);
98
+ if (!isWatch)
50
99
  return;
51
- const watcher = watch('.', {
100
+ const { ignored, paths } = createWatchConfig(options);
101
+ const watcher = watch(paths, {
52
102
  atomic: true,
53
103
  ignoreInitial: true,
54
- ignored: [/.+\.(?:test|spec)\.ts$/i]
104
+ ignored
55
105
  });
56
106
  watcher.on('change', async function () {
57
107
  process.stdout.write('\x1Bc');
@@ -63,18 +113,36 @@ async function handler() {
63
113
  //
64
114
  }
65
115
  }
66
- const { pid } = fork(join(process.cwd(), entry), {
67
- stdio: 'inherit',
68
- execArgv: [
69
- '--enable-source-maps',
70
- '--no-experimental-strip-types',
71
- '--import',
72
- pathToFileURL(join(import.meta.dirname, 'register.js')).href,
73
- ...nodeArgs
74
- ]
75
- });
76
- pids.add(pid);
116
+ spawn(entry, nodeArgs);
77
117
  });
78
118
  }
79
- program.action(handler);
80
- program.parse(process.argv);
119
+ function parseCsv(value) {
120
+ return value
121
+ .split(',')
122
+ .map((item) => item.trim())
123
+ .filter(Boolean);
124
+ }
125
+ program
126
+ .argument('<string>', 'Entrypoint file')
127
+ .argument('[nodeArgs...]', 'Specify the Node.js command and any additional arguments')
128
+ .action(async function (entry, nodeArgs, options) {
129
+ await handler(entry, options, nodeArgs, false);
130
+ });
131
+ program
132
+ .command('watch')
133
+ .option('--include <string>', 'Paths to be watched', parseCsv, DEFAULT_INCLUDE_PATHS)
134
+ .option('--exclude <string>', 'Paths to be ignored by watched', parseCsv, [
135
+ ...INTERNAL_IGNORED_PATHS,
136
+ ...DEFAULT_EXCLUDE_PATHS
137
+ ])
138
+ .option('--ext <string>', 'Extensions to be watched', parseCsv, DEFAULT_WATCH_EXTENSIONS)
139
+ .option('--source-root <string>', 'Source root to be watched', '.')
140
+ .argument('<string>', 'Entrypoint file')
141
+ .argument('[nodeArgs...]', 'Specify the Node.js command and any additional arguments')
142
+ .action(async function (entry, nodeArgs, options) {
143
+ await handler(entry, options, nodeArgs, true);
144
+ });
145
+ await program.parseAsync(process.argv).catch(function (err) {
146
+ console.error(err);
147
+ process.exit(1);
148
+ });
package/dist/loader.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { transformFile } from '@swc/core';
2
2
  import { fileURLToPath, pathToFileURL } from 'node:url';
3
3
  import { readFile } from 'node:fs/promises';
4
- import json5 from 'json5';
5
4
  import path from 'node:path';
5
+ import { parse } from './parse.js';
6
6
  import { resolveCache, existsWithCache } from './cache.js';
7
7
  const tsconfigCache = { paths: null, baseUrl: null };
8
8
  async function loadTSConfig() {
@@ -11,9 +11,9 @@ async function loadTSConfig() {
11
11
  }
12
12
  try {
13
13
  const data = await readFile(path.join(process.cwd(), 'tsconfig.json'), 'utf-8');
14
- const { compilerOptions: { paths, baseUrl } } = json5.parse(data);
14
+ const { compilerOptions: { paths, baseUrl } } = parse(data);
15
15
  tsconfigCache.paths = paths || null;
16
- tsconfigCache.baseUrl = baseUrl || process.cwd();
16
+ tsconfigCache.baseUrl = path.join(process.cwd(), baseUrl) || process.cwd();
17
17
  return tsconfigCache;
18
18
  }
19
19
  catch {
@@ -0,0 +1,4 @@
1
+ import { join } from 'node:path';
2
+ import { createRequire } from 'node:module';
3
+ const require = createRequire(import.meta.dirname);
4
+ export const { name, description, version } = require(join(import.meta.dirname, '..', 'package.json'));
package/dist/parse.js ADDED
@@ -0,0 +1,435 @@
1
+ /**
2
+ * JSON5 parse — zero dependencies, ESM + TypeScript
3
+ *
4
+ * Suporta toda a spec JSON5:
5
+ * - Chaves sem aspas (identificadores ES5/Unicode)
6
+ * - Strings single ou double quoted com escapes e line continuation
7
+ * - Comentários // e /* ... * /
8
+ * - Trailing commas em objetos e arrays
9
+ * - Números: hex, Infinity, NaN, +/- leading/trailing decimal
10
+ * - Reviver opcional (mesmo comportamento do JSON.parse nativo)
11
+ */
12
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
13
+ function err(ctx, msg) {
14
+ throw new SyntaxError(`JSON5: ${msg} at ${ctx.line}:${ctx.col}`);
15
+ }
16
+ function current(ctx) {
17
+ return ctx.src[ctx.pos] ?? '';
18
+ }
19
+ function peek(ctx, offset = 1) {
20
+ return ctx.src[ctx.pos + offset] ?? '';
21
+ }
22
+ function advance(ctx) {
23
+ const ch = ctx.src[ctx.pos++] ?? '';
24
+ if (ch === '\n') {
25
+ ctx.line++;
26
+ ctx.col = 1;
27
+ }
28
+ else {
29
+ ctx.col++;
30
+ }
31
+ return ch;
32
+ }
33
+ function startsWith(ctx, s) {
34
+ return ctx.src.startsWith(s, ctx.pos);
35
+ }
36
+ function consume(ctx, s) {
37
+ ctx.pos += s.length;
38
+ ctx.col += s.length;
39
+ }
40
+ // ─── Whitespace + Comments ────────────────────────────────────────────────────
41
+ const WHITESPACE = new Set([
42
+ ' ',
43
+ '\t',
44
+ '\r',
45
+ '\n',
46
+ '\u00A0',
47
+ '\u2028',
48
+ '\u2029',
49
+ '\uFEFF'
50
+ ]);
51
+ function skipWhitespaceAndComments(ctx) {
52
+ while (ctx.pos < ctx.src.length) {
53
+ const ch = current(ctx);
54
+ if (WHITESPACE.has(ch)) {
55
+ advance(ctx);
56
+ continue;
57
+ }
58
+ // Comentário de linha: //
59
+ if (ch === '/' && peek(ctx, 1) === '/') {
60
+ advance(ctx);
61
+ advance(ctx);
62
+ while (ctx.pos < ctx.src.length &&
63
+ current(ctx) !== '\n' &&
64
+ current(ctx) !== '\r' &&
65
+ current(ctx) !== '\u2028' &&
66
+ current(ctx) !== '\u2029')
67
+ advance(ctx);
68
+ continue;
69
+ }
70
+ // Comentário de bloco: /* ... */
71
+ if (ch === '/' && peek(ctx, 1) === '*') {
72
+ advance(ctx);
73
+ advance(ctx);
74
+ while (ctx.pos < ctx.src.length) {
75
+ if (current(ctx) === '*' && peek(ctx, 1) === '/') {
76
+ advance(ctx);
77
+ advance(ctx);
78
+ break;
79
+ }
80
+ advance(ctx);
81
+ }
82
+ continue;
83
+ }
84
+ break;
85
+ }
86
+ }
87
+ // ─── String ───────────────────────────────────────────────────────────────────
88
+ function parseString(ctx) {
89
+ const quote = advance(ctx); // ' ou "
90
+ let result = '';
91
+ while (ctx.pos < ctx.src.length) {
92
+ const ch = current(ctx);
93
+ if (ch === quote) {
94
+ advance(ctx);
95
+ return result;
96
+ }
97
+ if (ch === '\\') {
98
+ advance(ctx);
99
+ const esc = advance(ctx);
100
+ switch (esc) {
101
+ case '"':
102
+ result += '"';
103
+ break;
104
+ case "'":
105
+ result += "'";
106
+ break;
107
+ case '\\':
108
+ result += '\\';
109
+ break;
110
+ case '/':
111
+ result += '/';
112
+ break;
113
+ case 'b':
114
+ result += '\b';
115
+ break;
116
+ case 'f':
117
+ result += '\f';
118
+ break;
119
+ case 'n':
120
+ result += '\n';
121
+ break;
122
+ case 'r':
123
+ result += '\r';
124
+ break;
125
+ case 't':
126
+ result += '\t';
127
+ break;
128
+ case 'v':
129
+ result += '\v';
130
+ break;
131
+ case '0':
132
+ if (/[0-9]/.test(current(ctx)))
133
+ err(ctx, 'Octal escapes are not allowed');
134
+ result += '\0';
135
+ break;
136
+ case 'x': {
137
+ const hex = ctx.src.slice(ctx.pos, ctx.pos + 2);
138
+ if (!/^[0-9a-fA-F]{2}$/.test(hex))
139
+ err(ctx, 'Invalid hex escape sequence');
140
+ result += String.fromCharCode(parseInt(hex, 16));
141
+ ctx.pos += 2;
142
+ ctx.col += 2;
143
+ break;
144
+ }
145
+ case 'u': {
146
+ if (current(ctx) === '{') {
147
+ // \u{XXXXXX}
148
+ advance(ctx);
149
+ let hexStr = '';
150
+ while (ctx.pos < ctx.src.length && current(ctx) !== '}')
151
+ hexStr += advance(ctx);
152
+ if (current(ctx) !== '}')
153
+ err(ctx, 'Unterminated unicode escape');
154
+ advance(ctx);
155
+ const cp = parseInt(hexStr, 16);
156
+ if (isNaN(cp) || cp > 0x10ffff)
157
+ err(ctx, `Invalid unicode code point: ${hexStr}`);
158
+ result += String.fromCodePoint(cp);
159
+ }
160
+ else {
161
+ // \uXXXX
162
+ const hex = ctx.src.slice(ctx.pos, ctx.pos + 4);
163
+ if (!/^[0-9a-fA-F]{4}$/.test(hex))
164
+ err(ctx, 'Invalid unicode escape sequence');
165
+ result += String.fromCharCode(parseInt(hex, 16));
166
+ ctx.pos += 4;
167
+ ctx.col += 4;
168
+ }
169
+ break;
170
+ }
171
+ // Line continuation
172
+ case '\n':
173
+ case '\u2028':
174
+ case '\u2029':
175
+ break;
176
+ case '\r':
177
+ if (current(ctx) === '\n')
178
+ advance(ctx);
179
+ break;
180
+ default:
181
+ result += esc;
182
+ }
183
+ continue;
184
+ }
185
+ if (ch === '\n' || ch === '\r' || ch === '\u2028' || ch === '\u2029') {
186
+ err(ctx, 'Unterminated string literal');
187
+ }
188
+ result += advance(ctx);
189
+ }
190
+ err(ctx, 'Unterminated string literal');
191
+ }
192
+ // ─── Identifier (chave sem aspas) ─────────────────────────────────────────────
193
+ const IDENT_START = /[\p{L}\p{Nl}$_]/u;
194
+ const IDENT_PART = /[\p{L}\p{Nl}\p{Mn}\p{Mc}\p{Nd}\p{Pc}$_\u200C\u200D]/u;
195
+ function isIdentStart(ch) {
196
+ return IDENT_START.test(ch);
197
+ }
198
+ function isIdentPart(ch) {
199
+ return IDENT_PART.test(ch);
200
+ }
201
+ function parseIdentifier(ctx) {
202
+ const first = current(ctx);
203
+ if (!isIdentStart(first))
204
+ err(ctx, `Unexpected character: ${JSON.stringify(first)}`);
205
+ let name = advance(ctx);
206
+ while (ctx.pos < ctx.src.length && isIdentPart(current(ctx))) {
207
+ name += advance(ctx);
208
+ }
209
+ return name;
210
+ }
211
+ // ─── Number ───────────────────────────────────────────────────────────────────
212
+ function parseNumber(ctx) {
213
+ let numStr = '';
214
+ // Sinal opcional
215
+ if (current(ctx) === '+' || current(ctx) === '-') {
216
+ numStr += advance(ctx);
217
+ }
218
+ // Infinity
219
+ if (startsWith(ctx, 'Infinity')) {
220
+ consume(ctx, 'Infinity');
221
+ return numStr === '-' ? -Infinity : Infinity;
222
+ }
223
+ // NaN
224
+ if (startsWith(ctx, 'NaN')) {
225
+ consume(ctx, 'NaN');
226
+ return NaN;
227
+ }
228
+ // Hexadecimal: 0x / 0X
229
+ if (current(ctx) === '0' && (peek(ctx, 1) === 'x' || peek(ctx, 1) === 'X')) {
230
+ const sign = numStr; // pode ser '-' ou ''
231
+ numStr += advance(ctx); // '0'
232
+ numStr += advance(ctx); // 'x'
233
+ if (!/[0-9a-fA-F]/.test(current(ctx)))
234
+ err(ctx, 'Invalid hexadecimal number');
235
+ while (ctx.pos < ctx.src.length && /[0-9a-fA-F_]/.test(current(ctx))) {
236
+ const ch = advance(ctx);
237
+ if (ch !== '_')
238
+ numStr += ch;
239
+ }
240
+ const abs = parseInt(numStr, 16); // parseInt ignora o prefixo 0x corretamente
241
+ return sign === '-' ? -abs : abs;
242
+ }
243
+ // Parte inteira
244
+ while (ctx.pos < ctx.src.length && /[0-9_]/.test(current(ctx))) {
245
+ const ch = advance(ctx);
246
+ if (ch !== '_')
247
+ numStr += ch;
248
+ }
249
+ // Parte decimal
250
+ if (ctx.pos < ctx.src.length && current(ctx) === '.') {
251
+ numStr += advance(ctx);
252
+ while (ctx.pos < ctx.src.length && /[0-9_]/.test(current(ctx))) {
253
+ const ch = advance(ctx);
254
+ if (ch !== '_')
255
+ numStr += ch;
256
+ }
257
+ }
258
+ // Expoente
259
+ if (ctx.pos < ctx.src.length &&
260
+ (current(ctx) === 'e' || current(ctx) === 'E')) {
261
+ numStr += advance(ctx);
262
+ if (current(ctx) === '+' || current(ctx) === '-')
263
+ numStr += advance(ctx);
264
+ while (ctx.pos < ctx.src.length && /[0-9_]/.test(current(ctx))) {
265
+ const ch = advance(ctx);
266
+ if (ch !== '_')
267
+ numStr += ch;
268
+ }
269
+ }
270
+ const n = Number(numStr);
271
+ if (isNaN(n))
272
+ err(ctx, `Invalid number: ${numStr}`);
273
+ return n;
274
+ }
275
+ // ─── Value ────────────────────────────────────────────────────────────────────
276
+ function parseValue(ctx) {
277
+ skipWhitespaceAndComments(ctx);
278
+ if (ctx.pos >= ctx.src.length)
279
+ err(ctx, 'Unexpected end of input');
280
+ const ch = current(ctx);
281
+ if (ch === '"' || ch === "'")
282
+ return parseString(ctx);
283
+ if (ch === '{')
284
+ return parseObject(ctx);
285
+ if (ch === '[')
286
+ return parseArray(ctx);
287
+ if (startsWith(ctx, 'true')) {
288
+ consume(ctx, 'true');
289
+ return true;
290
+ }
291
+ if (startsWith(ctx, 'false')) {
292
+ consume(ctx, 'false');
293
+ return false;
294
+ }
295
+ if (startsWith(ctx, 'null')) {
296
+ consume(ctx, 'null');
297
+ return null;
298
+ }
299
+ if (ch === '+' ||
300
+ ch === '-' ||
301
+ ch === '.' ||
302
+ (ch >= '0' && ch <= '9') ||
303
+ startsWith(ctx, 'Infinity') ||
304
+ startsWith(ctx, 'NaN'))
305
+ return parseNumber(ctx);
306
+ err(ctx, `Unexpected token: ${JSON.stringify(ch)}`);
307
+ }
308
+ // ─── Object ───────────────────────────────────────────────────────────────────
309
+ function parseObject(ctx) {
310
+ advance(ctx); // {
311
+ const obj = {};
312
+ skipWhitespaceAndComments(ctx);
313
+ if (current(ctx) === '}') {
314
+ advance(ctx);
315
+ return obj;
316
+ }
317
+ while (ctx.pos < ctx.src.length) {
318
+ skipWhitespaceAndComments(ctx);
319
+ if (ctx.pos >= ctx.src.length)
320
+ err(ctx, 'Unterminated object');
321
+ // Trailing comma
322
+ if (current(ctx) === '}') {
323
+ advance(ctx);
324
+ return obj;
325
+ }
326
+ // Chave: string ou identifier
327
+ const ch = current(ctx);
328
+ const key = ch === '"' || ch === "'" ? parseString(ctx) : parseIdentifier(ctx);
329
+ skipWhitespaceAndComments(ctx);
330
+ if (current(ctx) !== ':')
331
+ err(ctx, `Expected ':' after key ${JSON.stringify(key)}`);
332
+ advance(ctx); // :
333
+ const entry = [key, parseValue(ctx)];
334
+ obj[entry[0]] = entry[1];
335
+ skipWhitespaceAndComments(ctx);
336
+ if (current(ctx) === ',') {
337
+ advance(ctx);
338
+ continue;
339
+ }
340
+ if (current(ctx) === '}') {
341
+ advance(ctx);
342
+ return obj;
343
+ }
344
+ err(ctx, 'Expected "," or "}" in object');
345
+ }
346
+ err(ctx, 'Unterminated object');
347
+ }
348
+ // ─── Array ────────────────────────────────────────────────────────────────────
349
+ function parseArray(ctx) {
350
+ advance(ctx); // [
351
+ const arr = [];
352
+ skipWhitespaceAndComments(ctx);
353
+ if (current(ctx) === ']') {
354
+ advance(ctx);
355
+ return arr;
356
+ }
357
+ while (ctx.pos < ctx.src.length) {
358
+ skipWhitespaceAndComments(ctx);
359
+ if (ctx.pos >= ctx.src.length)
360
+ err(ctx, 'Unterminated array');
361
+ // Trailing comma
362
+ if (current(ctx) === ']') {
363
+ advance(ctx);
364
+ return arr;
365
+ }
366
+ arr.push(parseValue(ctx));
367
+ skipWhitespaceAndComments(ctx);
368
+ if (current(ctx) === ',') {
369
+ advance(ctx);
370
+ continue;
371
+ }
372
+ if (current(ctx) === ']') {
373
+ advance(ctx);
374
+ return arr;
375
+ }
376
+ err(ctx, 'Expected "," or "]" in array');
377
+ }
378
+ err(ctx, 'Unterminated array');
379
+ }
380
+ function applyReviver(reviver, holder, key) {
381
+ const val = holder[key];
382
+ if (val !== null && typeof val === 'object') {
383
+ const obj = val;
384
+ for (const k of Object.keys(obj)) {
385
+ const newVal = applyReviver(reviver, obj, k);
386
+ if (newVal === undefined)
387
+ delete obj[k];
388
+ else
389
+ obj[k] = newVal;
390
+ }
391
+ }
392
+ return reviver.call(holder, key, val);
393
+ }
394
+ // ─── API pública ──────────────────────────────────────────────────────────────
395
+ /**
396
+ * Faz o parse de uma string JSON5 e retorna o valor JavaScript correspondente.
397
+ *
398
+ * @param source - String JSON5 a ser parseada.
399
+ * @param reviver - Função opcional chamada para cada par chave/valor (igual ao `JSON.parse`).
400
+ * @returns O valor JavaScript resultante tipado como `T`.
401
+ *
402
+ * @throws {SyntaxError} Se a string não for JSON5 válido.
403
+ *
404
+ * @example
405
+ * ```ts
406
+ * // Sem generic — retorna Json5Value
407
+ * const raw = parse(`{ host: 'localhost' }`)
408
+ *
409
+ * // Com generic — cast para o tipo desejado
410
+ * interface TsConfig {
411
+ * compilerOptions: {
412
+ * paths: Record<string, string[]>
413
+ * baseUrl: string
414
+ * }
415
+ * }
416
+ * const { compilerOptions: { paths, baseUrl } } = parse<TsConfig>(data)
417
+ * ```
418
+ */
419
+ export function parse(source, reviver) {
420
+ const ctx = {
421
+ src: String(source),
422
+ pos: 0,
423
+ line: 1,
424
+ col: 1
425
+ };
426
+ const result = parseValue(ctx);
427
+ skipWhitespaceAndComments(ctx);
428
+ if (ctx.pos < ctx.src.length) {
429
+ err(ctx, `Unexpected token after value: ${JSON.stringify(current(ctx))}`);
430
+ }
431
+ if (typeof reviver === 'function') {
432
+ return applyReviver(reviver, { '': result }, '');
433
+ }
434
+ return result;
435
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tsnite",
3
3
  "description": "TypeScript at full throttle—fast, safe, unstoppable. 🚀",
4
- "version": "0.0.18",
4
+ "version": "0.0.20",
5
5
  "keywords": [
6
6
  "cli",
7
7
  "esm",
@@ -34,16 +34,15 @@
34
34
  "dependencies": {
35
35
  "@swc/core": "^1.15.18",
36
36
  "chokidar": "^5.0.0",
37
- "commander": "^14.0.3",
38
- "json5": "^2.2.3"
37
+ "commander": "^14.0.3"
39
38
  },
40
39
  "devDependencies": {
41
- "@commitlint/cli": "^20.4.2",
42
- "@commitlint/config-conventional": "^20.4.2",
40
+ "@commitlint/cli": "^20.4.3",
41
+ "@commitlint/config-conventional": "^20.4.3",
43
42
  "@eslint/js": "^10.0.1",
44
43
  "@jest/globals": "^30.2.0",
45
44
  "@swc/jest": "^0.2.39",
46
- "eslint": "^10.0.2",
45
+ "eslint": "^10.0.3",
47
46
  "eslint-plugin-jest": "^29.15.0",
48
47
  "eslint-plugin-prettier": "^5.5.5",
49
48
  "globals": "^17.4.0",
@@ -51,7 +50,7 @@
51
50
  "jest": "^30.2.0",
52
51
  "jest-environment-node": "^30.2.0",
53
52
  "jest-mock-extended": "^4.0.0",
54
- "lint-staged": "^16.3.1",
53
+ "lint-staged": "^16.3.2",
55
54
  "prettier": "^3.8.1",
56
55
  "rimraf": "^6.1.3",
57
56
  "tsc-alias": "^1.8.16",