tsrx-typegen 0.2.2 → 0.3.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 +88 -71
- package/bin/tsrx-typegen.mjs +11 -0
- package/dist/cli.js +546 -0
- package/dist/pack-check.js +394 -0
- package/package.json +12 -4
- package/src/{cli.mjs → cli.ts} +37 -20
- package/src/{pack-check.mjs → pack-check.ts} +88 -30
|
@@ -4,7 +4,7 @@ import { tmpdir } from 'node:os'
|
|
|
4
4
|
import { dirname, extname, isAbsolute, join, relative, resolve, sep } from 'node:path'
|
|
5
5
|
|
|
6
6
|
const moduleSpecifierNodes = (ts, sourceFile) => {
|
|
7
|
-
const nodes = []
|
|
7
|
+
const nodes: any[] = []
|
|
8
8
|
const visit = (node) => {
|
|
9
9
|
if (
|
|
10
10
|
(ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) &&
|
|
@@ -50,15 +50,19 @@ const stringsIn = (value) => {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
const codeTarget = (target) =>
|
|
53
|
-
/\.(?:[cm]?[jt]sx?|tsrx)$/.test(target) && !/\.d\.[cm]?ts$/.test(target)
|
|
53
|
+
(!extname(target) || /\.(?:[cm]?[jt]sx?|tsrx)$/.test(target)) && !/\.d\.[cm]?ts$/.test(target)
|
|
54
54
|
|
|
55
|
-
function exportBranches(exportsMap, fallbackTypes = []) {
|
|
56
|
-
const branches = []
|
|
55
|
+
function exportBranches(exportsMap, fallbackTypes: any[] = []) {
|
|
56
|
+
const branches: any[] = []
|
|
57
57
|
for (const [subpath, mapping] of Object.entries(exportsMap ?? {})) {
|
|
58
58
|
const visit = (value, inheritedTypes) => {
|
|
59
59
|
if (typeof value === 'string') {
|
|
60
60
|
if (codeTarget(value)) {
|
|
61
|
-
branches.push({
|
|
61
|
+
branches.push({
|
|
62
|
+
subpath,
|
|
63
|
+
runtime: value,
|
|
64
|
+
types: inheritedTypes.length ? inheritedTypes : fallbackTypes,
|
|
65
|
+
})
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
return
|
|
@@ -68,22 +72,27 @@ function exportBranches(exportsMap, fallbackTypes = []) {
|
|
|
68
72
|
return
|
|
69
73
|
}
|
|
70
74
|
|
|
71
|
-
|
|
75
|
+
// An earlier condition cannot use a later sibling's type fallback.
|
|
76
|
+
let types = inheritedTypes
|
|
72
77
|
for (const [condition, child] of Object.entries(value)) {
|
|
73
|
-
if (condition
|
|
78
|
+
if (condition === 'types') {
|
|
79
|
+
if (!types.length) {
|
|
80
|
+
types = stringsIn(child)
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
74
83
|
visit(child, types)
|
|
75
84
|
}
|
|
76
85
|
}
|
|
77
86
|
}
|
|
78
87
|
|
|
79
|
-
visit(mapping,
|
|
88
|
+
visit(mapping, [])
|
|
80
89
|
}
|
|
81
90
|
|
|
82
91
|
return branches
|
|
83
92
|
}
|
|
84
93
|
|
|
85
94
|
function packageFiles(root) {
|
|
86
|
-
const files = []
|
|
95
|
+
const files: any[] = []
|
|
87
96
|
const visit = (dir) => {
|
|
88
97
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
89
98
|
if (entry.name === 'node_modules') {
|
|
@@ -104,11 +113,12 @@ function packageFiles(root) {
|
|
|
104
113
|
}
|
|
105
114
|
|
|
106
115
|
function expandTarget(target, files, packageRoot) {
|
|
107
|
-
|
|
116
|
+
const extensionless = !extname(target)
|
|
117
|
+
if (!target.includes('*') && !extensionless) {
|
|
108
118
|
return [{ path: target, capture: undefined }]
|
|
109
119
|
}
|
|
110
120
|
|
|
111
|
-
if ((target.match(/\*/g) ?? []).length
|
|
121
|
+
if ((target.match(/\*/g) ?? []).length > 1) {
|
|
112
122
|
throw new Error(`packed export target uses more than one wildcard: ${target}`)
|
|
113
123
|
}
|
|
114
124
|
|
|
@@ -118,19 +128,31 @@ function expandTarget(target, files, packageRoot) {
|
|
|
118
128
|
|
|
119
129
|
const pattern = target.slice(2)
|
|
120
130
|
const marker = pattern.indexOf('*')
|
|
121
|
-
const prefix = pattern.slice(0, marker)
|
|
122
|
-
const suffix = pattern.slice(marker + 1)
|
|
131
|
+
const prefix = marker === -1 ? pattern : pattern.slice(0, marker)
|
|
132
|
+
const suffix = marker === -1 ? '' : pattern.slice(marker + 1)
|
|
123
133
|
const matches = files.flatMap((file) => {
|
|
124
134
|
const path = relative(packageRoot, file).replaceAll('\\', '/')
|
|
135
|
+
// Extensionless source exports let the app choose platform siblings.
|
|
136
|
+
// Their wildcard captures are module stems, not source extensions.
|
|
137
|
+
if (!codeTarget(path) || (extensionless && !extname(path))) {
|
|
138
|
+
return []
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const matchedPath = extensionless ? path.slice(0, -extname(path).length) : path
|
|
125
142
|
if (
|
|
126
|
-
!
|
|
127
|
-
!
|
|
128
|
-
|
|
143
|
+
!matchedPath.startsWith(prefix) ||
|
|
144
|
+
!matchedPath.endsWith(suffix) ||
|
|
145
|
+
matchedPath.length < prefix.length + suffix.length ||
|
|
146
|
+
(marker === -1 && matchedPath !== pattern)
|
|
129
147
|
) {
|
|
130
148
|
return []
|
|
131
149
|
}
|
|
132
150
|
|
|
133
|
-
const capture =
|
|
151
|
+
const capture =
|
|
152
|
+
marker === -1
|
|
153
|
+
? undefined
|
|
154
|
+
: matchedPath.slice(prefix.length, matchedPath.length - suffix.length)
|
|
155
|
+
|
|
134
156
|
return [{ path: `./${path}`, capture }]
|
|
135
157
|
})
|
|
136
158
|
|
|
@@ -163,13 +185,11 @@ function packedPath(packageRoot, target) {
|
|
|
163
185
|
function sourceFile(ts, file) {
|
|
164
186
|
const extension = extname(file).toLowerCase()
|
|
165
187
|
const scriptKind =
|
|
166
|
-
extension === '.tsx'
|
|
188
|
+
extension === '.tsx' || extension === '.tsrx'
|
|
167
189
|
? ts.ScriptKind.TSX
|
|
168
190
|
: extension === '.jsx'
|
|
169
191
|
? ts.ScriptKind.JSX
|
|
170
|
-
:
|
|
171
|
-
? ts.ScriptKind.Deferred
|
|
172
|
-
: ts.ScriptKind.TS
|
|
192
|
+
: ts.ScriptKind.TS
|
|
173
193
|
|
|
174
194
|
return ts.createSourceFile(
|
|
175
195
|
file,
|
|
@@ -212,7 +232,7 @@ function localSourceModule(file, specifier) {
|
|
|
212
232
|
return null
|
|
213
233
|
}
|
|
214
234
|
|
|
215
|
-
function staticRuntimeExports(ts, file, seen = new Set()) {
|
|
235
|
+
function staticRuntimeExports(ts, file, seen: Set<any> = new Set()) {
|
|
216
236
|
const absolute = resolve(file)
|
|
217
237
|
if (seen.has(absolute)) {
|
|
218
238
|
return new Set()
|
|
@@ -221,7 +241,7 @@ function staticRuntimeExports(ts, file, seen = new Set()) {
|
|
|
221
241
|
seen.add(absolute)
|
|
222
242
|
|
|
223
243
|
const source = sourceFile(ts, absolute)
|
|
224
|
-
const names = new Set()
|
|
244
|
+
const names = new Set<any>()
|
|
225
245
|
const addBindingName = (name) => {
|
|
226
246
|
if (ts.isIdentifier(name)) {
|
|
227
247
|
names.add(name.text)
|
|
@@ -294,7 +314,13 @@ function staticRuntimeExports(ts, file, seen = new Set()) {
|
|
|
294
314
|
return names
|
|
295
315
|
}
|
|
296
316
|
|
|
297
|
-
function compilerExports(
|
|
317
|
+
function compilerExports(
|
|
318
|
+
ts,
|
|
319
|
+
program,
|
|
320
|
+
file,
|
|
321
|
+
cache: Map<any, any> = new Map(),
|
|
322
|
+
pending: Set<any> = new Set(),
|
|
323
|
+
) {
|
|
298
324
|
const absolute = resolve(file)
|
|
299
325
|
if (cache.has(absolute)) {
|
|
300
326
|
return cache.get(absolute)
|
|
@@ -315,8 +341,8 @@ function compilerExports(ts, program, file, cache = new Map(), pending = new Set
|
|
|
315
341
|
return undefined
|
|
316
342
|
}
|
|
317
343
|
|
|
318
|
-
const all = new Set()
|
|
319
|
-
const values = new Set()
|
|
344
|
+
const all = new Set<any>()
|
|
345
|
+
const values = new Set<any>()
|
|
320
346
|
for (const symbol of checker.getExportsOfModule(module)) {
|
|
321
347
|
all.add(symbol.name)
|
|
322
348
|
let resolved = symbol
|
|
@@ -335,9 +361,23 @@ function compilerExports(ts, program, file, cache = new Map(), pending = new Set
|
|
|
335
361
|
// target's symbols with their original flags — an ambient `declare const`
|
|
336
362
|
// still reads as a value even though no value binding crosses the edge.
|
|
337
363
|
// Subtract names whose only export edges are type-only.
|
|
338
|
-
const typeOnly = new Set()
|
|
364
|
+
const typeOnly = new Set<any>()
|
|
339
365
|
for (const statement of source.statements) {
|
|
340
|
-
if (!ts.isExportDeclaration(statement)
|
|
366
|
+
if (!ts.isExportDeclaration(statement)) {
|
|
367
|
+
continue
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (statement.exportClause && ts.isNamedExports(statement.exportClause)) {
|
|
371
|
+
for (const element of statement.exportClause.elements) {
|
|
372
|
+
if (statement.isTypeOnly || element.isTypeOnly) {
|
|
373
|
+
typeOnly.add(element.name.text)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
continue
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (!statement.isTypeOnly) {
|
|
341
381
|
continue
|
|
342
382
|
}
|
|
343
383
|
|
|
@@ -392,6 +432,20 @@ function moduleResolutionOptions(ts) {
|
|
|
392
432
|
allowImportingTsExtensions: true,
|
|
393
433
|
module: ts.ModuleKind.NodeNext,
|
|
394
434
|
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
435
|
+
// Same precedence as repository typecheck/typegen programs
|
|
436
|
+
// (packages/cli tsconfig-envs.mjs PLATFORM_MODULE_SUFFIXES):
|
|
437
|
+
// extensionless first, then every platform suffix.
|
|
438
|
+
moduleSuffixes: [
|
|
439
|
+
'',
|
|
440
|
+
'.web',
|
|
441
|
+
'.ios',
|
|
442
|
+
'.android',
|
|
443
|
+
'.mobile',
|
|
444
|
+
'.native',
|
|
445
|
+
'.macos',
|
|
446
|
+
'.linux',
|
|
447
|
+
'.windows',
|
|
448
|
+
],
|
|
395
449
|
noEmit: true,
|
|
396
450
|
skipLibCheck: true,
|
|
397
451
|
target: ts.ScriptTarget.Latest,
|
|
@@ -421,6 +475,10 @@ function verifyDeclarationClosure(ts, packageRoot, files, manifest, failures) {
|
|
|
421
475
|
)
|
|
422
476
|
} else if (!pathInside(packageRoot, resolve(resolved.resolvedFileName))) {
|
|
423
477
|
failures.push(`${relative(packageRoot, file)} resolves ${specifier} outside the package`)
|
|
478
|
+
} else if (!/\.d\.[cm]?ts$/.test(resolved.resolvedFileName)) {
|
|
479
|
+
failures.push(
|
|
480
|
+
`${relative(packageRoot, file)} resolves ${specifier} to implementation source; publish its declaration`,
|
|
481
|
+
)
|
|
424
482
|
}
|
|
425
483
|
|
|
426
484
|
continue
|
|
@@ -465,7 +523,7 @@ function verifyExportMap(ts, packageRoot, files, manifest, exportsMap, failures,
|
|
|
465
523
|
return
|
|
466
524
|
}
|
|
467
525
|
|
|
468
|
-
const pairs = []
|
|
526
|
+
const pairs: any[] = []
|
|
469
527
|
for (const branch of branches) {
|
|
470
528
|
if (!branch.types.length) {
|
|
471
529
|
failures.push(`${branch.subpath} runtime ${branch.runtime} has no types condition`)
|
|
@@ -546,7 +604,7 @@ function verifyExportMap(ts, packageRoot, files, manifest, exportsMap, failures,
|
|
|
546
604
|
}
|
|
547
605
|
|
|
548
606
|
export function verifyPackedPackage(ts, packageRoot) {
|
|
549
|
-
const failures = []
|
|
607
|
+
const failures: any[] = []
|
|
550
608
|
const manifestPath = join(packageRoot, 'package.json')
|
|
551
609
|
let manifest
|
|
552
610
|
try {
|