@pezkuwi/dev 0.84.2

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.
Files changed (37) hide show
  1. package/.skip-deno +0 -0
  2. package/README.md +547 -0
  3. package/config/eslint.js +160 -0
  4. package/config/eslint.rules.js +214 -0
  5. package/config/prettier.cjs +22 -0
  6. package/config/rollup.js +113 -0
  7. package/config/tsconfig.json +32 -0
  8. package/config/typedoc.cjs +18 -0
  9. package/package.json +107 -0
  10. package/scripts/polkadot-ci-ghact-build.mjs +540 -0
  11. package/scripts/polkadot-ci-ghact-docs.mjs +14 -0
  12. package/scripts/polkadot-ci-ghpages-force.mjs +43 -0
  13. package/scripts/polkadot-dev-build-docs.mjs +19 -0
  14. package/scripts/polkadot-dev-build-ts.mjs +1518 -0
  15. package/scripts/polkadot-dev-circular.mjs +29 -0
  16. package/scripts/polkadot-dev-clean-build.mjs +61 -0
  17. package/scripts/polkadot-dev-contrib.mjs +74 -0
  18. package/scripts/polkadot-dev-copy-dir.mjs +44 -0
  19. package/scripts/polkadot-dev-copy-to.mjs +53 -0
  20. package/scripts/polkadot-dev-deno-map.mjs +35 -0
  21. package/scripts/polkadot-dev-run-lint.mjs +40 -0
  22. package/scripts/polkadot-dev-run-node-ts.mjs +9 -0
  23. package/scripts/polkadot-dev-run-test.mjs +163 -0
  24. package/scripts/polkadot-dev-version.mjs +143 -0
  25. package/scripts/polkadot-dev-yarn-only.mjs +11 -0
  26. package/scripts/polkadot-exec-eslint.mjs +7 -0
  27. package/scripts/polkadot-exec-ghpages.mjs +11 -0
  28. package/scripts/polkadot-exec-ghrelease.mjs +7 -0
  29. package/scripts/polkadot-exec-node-test.mjs +368 -0
  30. package/scripts/polkadot-exec-rollup.mjs +7 -0
  31. package/scripts/polkadot-exec-tsc.mjs +7 -0
  32. package/scripts/polkadot-exec-webpack.mjs +7 -0
  33. package/scripts/util.mjs +540 -0
  34. package/tsconfig.build.json +18 -0
  35. package/tsconfig.config.json +14 -0
  36. package/tsconfig.scripts.json +14 -0
  37. package/tsconfig.spec.json +18 -0
@@ -0,0 +1,1518 @@
1
+ #!/usr/bin/env node
2
+ // Copyright 2017-2025 @polkadot/dev authors & contributors
3
+ // SPDX-License-Identifier: Apache-2.0
4
+
5
+ import JSON5 from 'json5';
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+ import process from 'node:process';
9
+ import ts from 'typescript';
10
+
11
+ import { copyDirSync, copyFileSync, DENO_EXT_PRE, DENO_LND_PRE, DENO_POL_PRE, engineVersionCmp, execPm, exitFatal, exitFatalEngine, logBin, mkdirpSync, PATHS_BUILD, readdirSync, rimrafSync } from './util.mjs';
12
+
13
+ /** @typedef {'babel' | 'esbuild' | 'swc' | 'tsc'} CompileType */
14
+ /** @typedef {{ bin?: Record<string, string>; browser?: string; bugs?: string; deno?: string; denoDependencies?: Record<string, string>; dependencies?: Record<string, string>; devDependencies?: Record<string, string>; electron?: string; engines?: { node?: string }; exports?: Record<string, unknown>; license?: string; homepage?: string; main?: string; module?: string; name?: string; optionalDependencies?: Record<string, string>; peerDependencies?: Record<string, string>; repository?: { directory?: string; type: 'git'; url: string; }; 'react-native'?: string; resolutions?: Record<string, string>; sideEffects?: boolean | string[]; scripts?: Record<string, string>; type?: 'module' | 'commonjs'; types?: string; version?: string; }} PkgJson */
15
+
16
+ const WP_CONFIGS = ['js', 'cjs'].map((e) => `webpack.config.${e}`);
17
+ const RL_CONFIGS = ['js', 'mjs', 'cjs'].map((e) => `rollup.config.${e}`);
18
+
19
+ logBin('polkadot-dev-build-ts');
20
+
21
+ exitFatalEngine();
22
+
23
+ // We need at least es2020 for dynamic imports. Settings here needs to align with
24
+ // those in packages/dev-ts/src/loader & packages/dev/config/tsconfig
25
+ //
26
+ // Node 14 === es2020 (w/ dynamic imports)
27
+ // Node 16 === es2021
28
+ // Node 18/20 === es2022 (w/ private fields)
29
+ //
30
+ // https://github.com/tsconfig/bases/tree/main/bases
31
+ const TARGET_TSES = ts.ScriptTarget.ES2022;
32
+ const TARGET_NODE = '>=18';
33
+
34
+ const IGNORE_IMPORTS = [
35
+ // node (new-style)
36
+ ...['assert', 'child_process', 'crypto', 'fs', 'module', 'os', 'path', 'process', 'readline', 'test', 'url', 'util'].map((m) => `node:${m}`),
37
+ // other
38
+ '@testing-library/react',
39
+ 'react', 'react-native', 'styled-components'
40
+ ];
41
+
42
+ /**
43
+ * webpack build
44
+ */
45
+ function buildWebpack () {
46
+ const config = WP_CONFIGS.find((c) => fs.existsSync(path.join(process.cwd(), c)));
47
+
48
+ execPm(`polkadot-exec-webpack --config ${config} --mode production`);
49
+ }
50
+
51
+ /**
52
+ * compile via tsc, either via supplied config or default
53
+ *
54
+ * @param {CompileType} compileType
55
+ * @param {'cjs' | 'esm'} type
56
+ */
57
+ async function compileJs (compileType, type) {
58
+ const buildDir = path.join(process.cwd(), `build-${compileType}-${type}`);
59
+
60
+ mkdirpSync(buildDir);
61
+
62
+ const files = readdirSync('src', ['.ts', '.tsx']).filter((f) =>
63
+ !['.d.ts', '.manual.ts', '.spec.ts', '.spec.tsx', '.test.ts', '.test.tsx', 'mod.ts'].some((e) =>
64
+ f.endsWith(e)
65
+ )
66
+ );
67
+
68
+ if (compileType === 'tsc') {
69
+ await timeIt(`Successfully compiled ${compileType} ${type}`, () => {
70
+ files.forEach((filename) => {
71
+ // split src prefix, replace .ts extension with .js
72
+ const outFile = path.join(buildDir, filename.split(/[\\/]/).slice(1).join('/').replace(/\.tsx?$/, '.js'));
73
+
74
+ const source = fs.readFileSync(filename, 'utf-8');
75
+
76
+ // compile with the options aligning with our tsconfig
77
+ const { outputText } = ts.transpileModule(source, {
78
+ compilerOptions: {
79
+ esModuleInterop: true,
80
+ importHelpers: true,
81
+ jsx: filename.endsWith('.tsx')
82
+ ? ts.JsxEmit.ReactJSX
83
+ : undefined,
84
+ module: type === 'cjs'
85
+ ? ts.ModuleKind.CommonJS
86
+ : ts.ModuleKind.ESNext,
87
+ moduleResolution: ts.ModuleResolutionKind.NodeNext,
88
+ target: TARGET_TSES
89
+ }
90
+ });
91
+
92
+ mkdirpSync(path.dirname(outFile));
93
+ fs.writeFileSync(outFile, outputText);
94
+ });
95
+ });
96
+ } else {
97
+ throw new Error(`Unknown --compiler ${compileType}`);
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Writes a package.json file
103
+ *
104
+ * @param {string} path
105
+ * @param {PkgJson} json
106
+ */
107
+ function witeJson (path, json) {
108
+ fs.writeFileSync(path, `${JSON.stringify(json, null, 2)}\n`);
109
+ }
110
+
111
+ /**
112
+ * Adjust all imports to have .js extensions
113
+ *
114
+ * @param {string} _pkgCwd
115
+ * @param {PkgJson} _pkgJson
116
+ * @param {string} dir
117
+ * @param {string} f
118
+ * @param {boolean} [_isDeclare]
119
+ * @returns {string | null}
120
+ */
121
+ function adjustJsPath (_pkgCwd, _pkgJson, dir, f, _isDeclare) {
122
+ if (f.startsWith('.')) {
123
+ if (f.endsWith('.js') || f.endsWith('.json')) {
124
+ // ignore, these are already fully-specified
125
+ return null;
126
+ }
127
+
128
+ const dirPath = path.join(process.cwd(), dir, f);
129
+ const jsFile = `${f}.js`;
130
+ const jsPath = path.join(process.cwd(), dir, jsFile);
131
+
132
+ if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
133
+ // this is a directory, append index.js
134
+ return `${f}/index.js`;
135
+ } else if (fs.existsSync(jsPath)) {
136
+ // local source file
137
+ return jsFile;
138
+ }
139
+ }
140
+
141
+ // do not adjust
142
+ return null;
143
+ }
144
+
145
+ /**
146
+ * Adjust all @polkadot imports to have .ts extensions (for Deno)
147
+ *
148
+ * @param {string} pkgCwd
149
+ * @param {PkgJson} pkgJson
150
+ * @param {string} dir
151
+ * @param {string} f
152
+ * @param {boolean} [isDeclare]
153
+ * @returns {string | null}
154
+ */
155
+ function adjustDenoPath (pkgCwd, pkgJson, dir, f, isDeclare) {
156
+ if (f.startsWith('@polkadot')) {
157
+ const parts = f.split('/');
158
+ const thisPkg = parts.slice(0, 2).join('/');
159
+ const subPath = parts.slice(2).join('/');
160
+ const pjsPath = `${DENO_POL_PRE}/${thisPkg.replace('@polkadot/', '')}`;
161
+
162
+ if (subPath.includes("' assert { type:")) {
163
+ // these are for type asserts, we keep the assert
164
+ return `${pjsPath}/${subPath}`;
165
+ } else if (parts.length === 2) {
166
+ // if we only have 2 parts, we add deno/mod.ts
167
+ return `${pjsPath}/mod.ts`;
168
+ }
169
+
170
+ // first we check in packages/* to see if we have this one
171
+ const pkgPath = path.join(pkgCwd, '..', parts[1]);
172
+
173
+ if (fs.existsSync(pkgPath)) {
174
+ // aha, this is a package in the same repo, search src
175
+ const checkPath = path.join(pkgPath, 'src', subPath);
176
+
177
+ if (fs.existsSync(checkPath)) {
178
+ if (fs.statSync(checkPath).isDirectory()) {
179
+ // this is a directory, append index.ts
180
+ return `${pjsPath}/${subPath}/index.ts`;
181
+ }
182
+
183
+ // as-is, the path exists
184
+ return `${DENO_POL_PRE}/${subPath}`;
185
+ } else if (!fs.existsSync(`${checkPath}.ts`)) {
186
+ exitFatal(`Unable to find ${checkPath}.ts`);
187
+ }
188
+
189
+ return `${pjsPath}/${subPath}.ts`;
190
+ }
191
+
192
+ // now we check node_modules
193
+ const nodePath = path.join(pkgCwd, '../../node_modules', thisPkg);
194
+
195
+ if (fs.existsSync(nodePath)) {
196
+ // aha, this is a package in the same repo
197
+ const checkPath = path.join(nodePath, subPath);
198
+
199
+ if (fs.existsSync(checkPath)) {
200
+ if (fs.statSync(checkPath).isDirectory()) {
201
+ // this is a directory, append index.ts
202
+ return `${pjsPath}/${subPath}/index.ts`;
203
+ }
204
+
205
+ // as-is, it exists
206
+ return `${pjsPath}/${subPath}`;
207
+ } else if (!fs.existsSync(`${checkPath}.js`)) {
208
+ exitFatal(`Unable to find ${checkPath}.js`);
209
+ }
210
+
211
+ return `${pjsPath}/${subPath}.ts`;
212
+ }
213
+
214
+ // we don't know what to do here :(
215
+ exitFatal(`Unable to find ${f}`);
216
+ } else if (f.startsWith('.')) {
217
+ if (f.endsWith('.ts') || f.endsWith('.tsx') || f.endsWith('.json')) {
218
+ // ignore, these are already fully-specified
219
+ return null;
220
+ } else if (f.endsWith('.js')) {
221
+ if (f.includes('./cjs/')) {
222
+ // import from cjs/, change it to deno
223
+ return f.replace('/cjs/', '/deno/');
224
+ }
225
+
226
+ const tsFile = f.replace('.js', '.ts');
227
+ const tsxFile = f.replace('.js', '.tsx');
228
+
229
+ if (fs.existsSync(path.join(process.cwd(), dir, tsFile))) {
230
+ // we have a .ts file for this one, rename
231
+ return tsFile;
232
+ } else if (fs.existsSync(path.join(process.cwd(), dir, tsxFile))) {
233
+ // we have a .tsx file for this one, rename
234
+ return tsxFile;
235
+ }
236
+
237
+ // leave the other paths as-is
238
+ return null;
239
+ }
240
+
241
+ const dirPath = path.join(process.cwd(), dir, f);
242
+ const tsFile = `${f}.ts`;
243
+ const tsxFile = `${f}.tsx`;
244
+ const tsPath = path.join(process.cwd(), dir, tsFile);
245
+ const tsxPath = path.join(process.cwd(), dir, tsxFile);
246
+
247
+ if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
248
+ // this is a directory, append index.ts
249
+ return fs.existsSync(path.join(dirPath, 'index.tsx'))
250
+ ? `${f}/index.tsx`
251
+ : `${f}/index.ts`;
252
+ } else if (fs.existsSync(tsPath)) {
253
+ // local source file
254
+ return tsFile;
255
+ } else if (fs.existsSync(tsxPath)) {
256
+ // local source file
257
+ return tsxFile;
258
+ }
259
+
260
+ // fully-specified file, keep it as-is (linting picks up invalids)
261
+ return null;
262
+ } else if (f.startsWith('node:')) {
263
+ // Since Deno 1.28 the node: specifiers is supported out-of-the-box
264
+ // so we just return and use these as-is
265
+ return f;
266
+ }
267
+
268
+ const depParts = f.split('/');
269
+ const depNameLen = f.startsWith('@')
270
+ ? 2
271
+ : 1;
272
+ const depName = depParts.slice(0, depNameLen).join('/');
273
+ let depPath = depParts.length > depNameLen
274
+ ? '/' + depParts.slice(depNameLen).join('/')
275
+ : null;
276
+
277
+ const depVersion = pkgJson.dependencies?.[depName] && pkgJson.dependencies[depName] !== '*'
278
+ ? pkgJson.dependencies[depName]
279
+ : pkgJson.peerDependencies?.[depName]
280
+ ? pkgJson.peerDependencies[depName]
281
+ : pkgJson.optionalDependencies?.[depName]
282
+ ? pkgJson.optionalDependencies[depName]
283
+ : pkgJson.devDependencies
284
+ ? pkgJson.devDependencies[depName]
285
+ : null;
286
+ let version = null;
287
+
288
+ if (depVersion) {
289
+ version = depVersion.replace('^', '').replace('~', '');
290
+ } else if (isDeclare) {
291
+ return f;
292
+ }
293
+
294
+ let [denoDep, ...denoPath] = pkgJson.denoDependencies?.[depName]
295
+ ? pkgJson.denoDependencies[depName].split('/')
296
+ : [null];
297
+
298
+ if (!denoDep) {
299
+ if (IGNORE_IMPORTS.includes(depName)) {
300
+ // ignore, we handle this below
301
+ } else if (depVersion) {
302
+ // Here we use the npm: specifier (available since Deno 1.28)
303
+ //
304
+ // FIXME We cannot enable this until there is support for git deps
305
+ // https://github.com/denoland/deno/issues/18557
306
+ // This is used by @zondax/ledger-substrate
307
+ // return `npm:${depName}@${depVersion}${depPath || ''}`;
308
+ } else {
309
+ exitFatal(`Unknown Deno versioned package '${f}' inside ${pkgJson.name}`);
310
+ }
311
+ } else if (denoDep === 'x') {
312
+ denoDep = `x/${denoPath[0]}`;
313
+ denoPath = denoPath.slice(1);
314
+
315
+ if (!denoDep.includes('@')) {
316
+ denoDep = `${denoDep}@${version}`;
317
+ } else if (denoDep.includes('{{VERSION}}')) {
318
+ if (!version) {
319
+ throw new Error(`Unable to extract version for deno.land/${denoDep}`);
320
+ }
321
+
322
+ denoDep = denoDep.replace('{{VERSION}}', version);
323
+ }
324
+ }
325
+
326
+ // Add JS specifier if required
327
+ if (depPath) {
328
+ depPath += '.js';
329
+ }
330
+
331
+ return denoDep
332
+ ? `${DENO_LND_PRE}/${denoDep}${depPath || `/${denoPath.length ? denoPath.join('/') : 'mod.ts'}`}`
333
+ : `${DENO_EXT_PRE}/${depName}${version ? `@${version}` : ''}${depPath || ''}`;
334
+ }
335
+
336
+ /**
337
+ * @param {string} dir
338
+ * @param {string} pkgCwd
339
+ * @param {PkgJson} pkgJson
340
+ * @param {(pkgCwd: string, pkgJson: PkgJson, f: string, dir: string, isDeclare?: boolean) => string | null} replacer
341
+ * @returns {void}
342
+ */
343
+ function rewriteImports (dir, pkgCwd, pkgJson, replacer) {
344
+ if (!fs.existsSync(dir)) {
345
+ return;
346
+ }
347
+
348
+ fs
349
+ .readdirSync(dir)
350
+ .forEach((p) => {
351
+ const thisPath = path.join(process.cwd(), dir, p);
352
+
353
+ if (fs.statSync(thisPath).isDirectory()) {
354
+ rewriteImports(`${dir}/${p}`, pkgCwd, pkgJson, replacer);
355
+ } else if (thisPath.endsWith('.spec.js') || thisPath.endsWith('.spec.ts')) {
356
+ // we leave specs as-is
357
+ } else if (thisPath.endsWith('.js') || thisPath.endsWith('.ts') || thisPath.endsWith('.tsx') || thisPath.endsWith('.md')) {
358
+ fs.writeFileSync(
359
+ thisPath,
360
+ fs
361
+ .readFileSync(thisPath, 'utf8')
362
+ .split('\n')
363
+ .filter((line) => !line.startsWith('//'))
364
+ .map((line) =>
365
+ line
366
+ // handle import/export
367
+ .replace(/(import|export) (.*) from '(.*)'/g, (o, t, a, f) => {
368
+ const adjusted = replacer(pkgCwd, pkgJson, dir, f);
369
+
370
+ return adjusted
371
+ ? `${t} ${a} from '${adjusted}'`
372
+ : o;
373
+ })
374
+ // handle augmented inputs
375
+ .replace(/(import|declare module) '(.*)'/g, (o, t, f) => {
376
+ const adjusted = replacer(pkgCwd, pkgJson, dir, f, t !== 'import');
377
+
378
+ return adjusted
379
+ ? `${t} '${adjusted}'`
380
+ : o;
381
+ })
382
+ // handle dynamic imports
383
+ .replace(/( import|^import)\('(.*)'\)/g, (o, t, f) => {
384
+ const adjusted = replacer(pkgCwd, pkgJson, dir, f);
385
+
386
+ return adjusted
387
+ ? `${t}('${adjusted}')`
388
+ : o;
389
+ })
390
+ )
391
+ .join('\n')
392
+ );
393
+ }
394
+ });
395
+ }
396
+
397
+ /**
398
+ * Adds the `import { Buffer } from 'node:buffer';` to files that use `Buffer`.
399
+ *
400
+ * @param {string} dir - Directory to traverse.
401
+ * @param {string} pkgCwd - Current working directory of the package.
402
+ */
403
+ function addBufferImportForDeno (dir, pkgCwd) {
404
+ if (!fs.existsSync(dir)) {
405
+ return;
406
+ }
407
+
408
+ fs.readdirSync(dir).forEach((fileName) => {
409
+ const filePath = path.join(dir, fileName);
410
+
411
+ if (fs.statSync(filePath).isDirectory()) {
412
+ // Recursively handle subdirectories
413
+ addBufferImportForDeno(filePath, pkgCwd);
414
+ } else if (filePath.endsWith('.ts') || filePath.endsWith('.tsx')) {
415
+ const content = fs.readFileSync(filePath, 'utf-8');
416
+
417
+ if (content.includes('Buffer') && !content.includes("import { Buffer } from 'node:buffer';")) {
418
+ const updatedContent = `import { Buffer } from 'node:buffer';\n\n${content}`;
419
+
420
+ fs.writeFileSync(filePath, updatedContent, 'utf-8');
421
+ }
422
+ }
423
+ });
424
+ }
425
+
426
+ function buildDeno () {
427
+ const pkgCwd = process.cwd();
428
+
429
+ if (!fs.existsSync(path.join(pkgCwd, 'src/mod.ts'))) {
430
+ return;
431
+ }
432
+
433
+ // copy the sources as-is
434
+ copyDirSync('src', 'build-deno', [], ['.spec.ts', '.spec.tsx', '.test.ts', '.test.tsx']);
435
+ copyFileSync('README.md', 'build-deno');
436
+
437
+ // remove unneeded directories
438
+ rimrafSync('build-deno/cjs');
439
+
440
+ addBufferImportForDeno('build-deno', pkgCwd);
441
+ }
442
+
443
+ /**
444
+ * @param {string} [value]
445
+ * @returns {string}
446
+ */
447
+ function relativePath (value) {
448
+ return `${value && value.startsWith('.') ? value : './'}${value}`.replace(/\/\//g, '/');
449
+ }
450
+
451
+ /**
452
+ * creates an entry for the cjs/esm name
453
+ *
454
+ * @param {string} rootDir
455
+ * @param {string} [jsPath]
456
+ * @param {boolean} [noTypes]
457
+ * @returns {[string, Record<string, unknown> | string]}
458
+ */
459
+ function createMapEntry (rootDir, jsPath = '', noTypes) {
460
+ jsPath = relativePath(jsPath);
461
+
462
+ const typesPath = jsPath.replace('.js', '.d.ts');
463
+ const cjsPath = jsPath.replace('./', './cjs/');
464
+ const cjsTypesPath = typesPath.replace('./', './cjs/');
465
+ const hasCjs = fs.existsSync(path.join(rootDir, cjsPath));
466
+ const hasTypesCjs = fs.existsSync(path.join(rootDir, cjsTypesPath));
467
+ const hasTypes = !hasTypesCjs && !noTypes && jsPath.endsWith('.js') && fs.existsSync(path.join(rootDir, typesPath));
468
+ const field = hasCjs
469
+ ? {
470
+ // As per TS, the types key needs to be first
471
+ ...(
472
+ hasTypes
473
+ ? { types: typesPath }
474
+ : {}
475
+ ),
476
+ // bundler-specific path, eg. webpack & rollup
477
+ ...(
478
+ jsPath.endsWith('.js')
479
+ ? hasTypesCjs
480
+ // eslint-disable-next-line sort-keys
481
+ ? { module: { types: typesPath, default: jsPath } }
482
+ : { module: jsPath }
483
+ : {}
484
+ ),
485
+ require: hasTypesCjs
486
+ // eslint-disable-next-line sort-keys
487
+ ? { types: cjsTypesPath, default: cjsPath }
488
+ : cjsPath,
489
+ // eslint-disable-next-line sort-keys
490
+ default: hasTypesCjs
491
+ // eslint-disable-next-line sort-keys
492
+ ? { types: typesPath, default: jsPath }
493
+ : jsPath
494
+ }
495
+ : hasTypes
496
+ ? {
497
+ types: typesPath,
498
+ // eslint-disable-next-line sort-keys
499
+ default: jsPath
500
+ }
501
+ : jsPath;
502
+
503
+ if (jsPath.endsWith('.js')) {
504
+ if (jsPath.endsWith('/index.js')) {
505
+ return [jsPath.replace('/index.js', ''), field];
506
+ } else {
507
+ return [jsPath.replace('.js', ''), field];
508
+ }
509
+ }
510
+
511
+ return [jsPath, field];
512
+ }
513
+
514
+ /**
515
+ * copies all output files into the build directory
516
+ *
517
+ * @param {CompileType} compileType
518
+ * @param {string} dir
519
+ */
520
+ function copyBuildFiles (compileType, dir) {
521
+ mkdirpSync('build/cjs');
522
+
523
+ // copy package info stuff
524
+ copyFileSync(['package.json', 'README.md'], 'build');
525
+ copyFileSync('../../LICENSE', 'build');
526
+
527
+ // copy interesting files
528
+ copyDirSync('src', 'build', ['.patch', '.js', '.cjs', '.mjs', '.json', '.d.ts', '.d.cts', '.d.mts', '.css', '.gif', '.hbs', '.md', '.jpg', '.png', '.rs', '.svg']);
529
+
530
+ // copy all *.d.ts files
531
+ const dtsPaths = ['build-tsc', path.join('../../build', dir, 'src'), path.join('../../build/packages', dir, 'src')];
532
+
533
+ copyDirSync(dtsPaths, 'build', ['.d.ts']);
534
+ copyDirSync(dtsPaths, 'build/cjs', ['.d.ts']);
535
+
536
+ // copy all from build-{babel|swc|tsc|...}-esm to build
537
+ copyDirSync(`build-${compileType}-esm`, 'build');
538
+
539
+ // copy from build-{babel|swc|tsc|...}-cjs to build/cjs (js-only)
540
+ copyDirSync(`build-${compileType}-cjs`, 'build/cjs', ['.js']);
541
+ }
542
+
543
+ /**
544
+ * remove all extra files that were generated as part of the build
545
+ *
546
+ * @param {string} [extra]
547
+ * @param {string[][]} [invalids]
548
+ */
549
+ function deleteBuildFiles (extra = '', invalids) {
550
+ const isTopLevel = !invalids;
551
+
552
+ invalids ??= [];
553
+
554
+ const buildDir = 'build';
555
+ const currDir = extra
556
+ ? path.join('build', extra)
557
+ : buildDir;
558
+ const allFiles = fs
559
+ .readdirSync(currDir)
560
+ .map((jsName) => {
561
+ const jsPath = `${extra}/${jsName}`;
562
+ const fullPathEsm = path.join(buildDir, jsPath);
563
+
564
+ return [jsName, jsPath, fullPathEsm];
565
+ });
566
+
567
+ // We want the build config tweaked to not allow these, so error-out
568
+ // when they are found (it indicates a config failure)
569
+ invalids.push(...allFiles.filter(([jsName, jsPath]) =>
570
+ // no tests
571
+ (
572
+ ['.manual.', '.spec.', '.test.'].some((t) => jsName.includes(t)) &&
573
+ // we explicitly exclude test paths, just treat as artifacts
574
+ !jsPath.includes('/test/')
575
+ ) ||
576
+ // no deno mod.ts compiles
577
+ ['mod.js', 'mod.d.ts', 'mod.ts'].some((e) => jsName === e)
578
+ ));
579
+
580
+ allFiles.forEach(([jsName, jsPath, fullPathEsm]) => {
581
+ const toDelete = (
582
+ // no test paths
583
+ jsPath.includes('/test/') ||
584
+ // no rust files
585
+ ['.rs'].some((e) => jsName.endsWith(e)) ||
586
+ // no tests
587
+ ['.manual.', '.spec.', '.test.'].some((t) => jsName.includes(t)) ||
588
+ // no .d.ts compiled outputs
589
+ ['.d.js', '.d.cjs', '.d.mjs'].some((e) => jsName.endsWith(e)) ||
590
+ // no deno mod.ts compiles
591
+ ['mod.js', 'mod.d.ts', 'mod.ts'].some((e) => jsName === e) ||
592
+ (
593
+ // .d.ts without .js as an output
594
+ jsName.endsWith('.d.ts') &&
595
+ !['.js', '.cjs', '.mjs'].some((e) =>
596
+ fs.existsSync(path.join(buildDir, jsPath.replace('.d.ts', e)))
597
+ )
598
+ )
599
+ );
600
+
601
+ if (fs.statSync(fullPathEsm).isDirectory()) {
602
+ deleteBuildFiles(jsPath, invalids);
603
+
604
+ PATHS_BUILD.forEach((b) => {
605
+ // remove all empty directories
606
+ const otherPath = path.join(`${buildDir}${b}`, jsPath);
607
+
608
+ if (fs.existsSync(otherPath) && fs.readdirSync(otherPath).length === 0) {
609
+ rimrafSync(otherPath);
610
+ }
611
+ });
612
+ } else if (toDelete) {
613
+ PATHS_BUILD.forEach((b) => {
614
+ // check in the other build outputs and remove
615
+ // (for deno we also want the spec copies)
616
+ const otherPath = path.join(`${buildDir}${b}`, jsPath);
617
+ const otherTs = otherPath.replace(/.spec.js$/, '.spec.ts');
618
+
619
+ [otherPath, otherTs].forEach((f) => rimrafSync(f));
620
+ });
621
+ }
622
+ }, []);
623
+
624
+ if (isTopLevel && invalids.length) {
625
+ throw new Error(`Invalid build outputs found in ${process.cwd()}: ${invalids.map(([,, p]) => p).join(', ')} (These should be excluded via a noEmit option in the project config)`);
626
+ }
627
+ }
628
+
629
+ /**
630
+ * find the names of all the files in a certain directory
631
+ *
632
+ * @param {string} buildDir
633
+ * @param {string} [extra]
634
+ * @param {string[]} [exclude]
635
+ * @returns {[string, Record<String, unknown> | string][]}
636
+ */
637
+ function findFiles (buildDir, extra = '', exclude = []) {
638
+ const currDir = extra
639
+ ? path.join(buildDir, extra)
640
+ : buildDir;
641
+
642
+ return fs
643
+ .readdirSync(currDir)
644
+ .filter((f) => !exclude.includes(f))
645
+ .reduce((/** @type {[string, Record<String, unknown> | string][]} */ all, jsName) => {
646
+ const jsPath = `${extra}/${jsName}`;
647
+ const fullPathEsm = path.join(buildDir, jsPath);
648
+
649
+ if (fs.statSync(fullPathEsm).isDirectory()) {
650
+ findFiles(buildDir, jsPath).forEach((e) => all.push(e));
651
+ } else {
652
+ // this is not mapped to a compiled .js file (where we have dual esm/cjs mappings)
653
+ all.push(createMapEntry(buildDir, jsPath));
654
+ }
655
+
656
+ return all;
657
+ }, []);
658
+ }
659
+
660
+ /**
661
+ * Tweak any CJS imports to import from the actual cjs path
662
+ */
663
+ function tweakCjsPaths () {
664
+ readdirSync('build/cjs', ['.js']).forEach((thisPath) => {
665
+ fs.writeFileSync(
666
+ thisPath,
667
+ fs
668
+ .readFileSync(thisPath, 'utf8')
669
+ // This is actually problematic - while we don't use non-js imports (mostly),
670
+ // this would also match those, which creates issues. For the most part we only
671
+ // actually should only care about packageInfo, so add this one explicitly. If we
672
+ // do use path-imports for others, rather adjust them at that specific point
673
+ // .replace(
674
+ // /require\("@polkadot\/([a-z-]*)\/(.*)"\)/g,
675
+ // 'require("@polkadot/$1/cjs/$2")'
676
+ // )
677
+ .replace(
678
+ /require\("@polkadot\/([a-z-]*)\/packageInfo"\)/g,
679
+ 'require("@polkadot/$1/cjs/packageInfo")'
680
+ )
681
+ );
682
+ });
683
+ }
684
+
685
+ /**
686
+ * Adjusts the packageInfo.js files for the target output
687
+ *
688
+ * @param {CompileType} compileType
689
+ */
690
+ function tweakPackageInfo (compileType) {
691
+ // Hack around some bundler issues, in this case Vite which has import.meta.url
692
+ // as undefined in production contexts (and subsequently makes URL fail)
693
+ // See https://github.com/vitejs/vite/issues/5558
694
+ const esmPathname = 'new URL(import.meta.url).pathname';
695
+ const esmDirname = `(import.meta && import.meta.url) ? ${esmPathname}.substring(0, ${esmPathname}.lastIndexOf('/') + 1) : 'auto'`;
696
+ const cjsDirname = "typeof __dirname === 'string' ? __dirname : 'auto'";
697
+
698
+ ['esm', 'cjs'].forEach((jsType) => {
699
+ const infoFile = `build-${compileType}-${jsType}/packageInfo.js`;
700
+
701
+ fs.writeFileSync(
702
+ infoFile,
703
+ fs
704
+ .readFileSync(infoFile, 'utf8')
705
+ .replace(
706
+ "type: 'auto'",
707
+ `type: '${jsType}'`
708
+ )
709
+ .replace(
710
+ "path: 'auto'",
711
+ `path: ${jsType === 'cjs' ? cjsDirname : esmDirname}`
712
+ )
713
+ );
714
+ });
715
+
716
+ const denoFile = path.join('build-deno', 'packageInfo.ts');
717
+
718
+ // Not all packages are built for deno (if no mod.ts, don't build)
719
+ if (fs.existsSync(denoFile)) {
720
+ fs.writeFileSync(
721
+ denoFile,
722
+ fs
723
+ .readFileSync(denoFile, 'utf8')
724
+ .replace(
725
+ "type: 'auto'",
726
+ "type: 'deno'"
727
+ )
728
+ .replace(
729
+ "path: 'auto'",
730
+ `path: ${esmPathname}`
731
+ )
732
+ );
733
+ }
734
+ }
735
+
736
+ /**
737
+ * Adjusts the order of fiels in the package.json
738
+ *
739
+ * @param {Record<string, unknown>} pkgJson
740
+ * @param {string[]} fields
741
+ */
742
+ function moveFields (pkgJson, fields) {
743
+ fields.forEach((k) => {
744
+ if (typeof pkgJson[k] !== 'undefined') {
745
+ const value = pkgJson[k];
746
+
747
+ delete pkgJson[k];
748
+
749
+ pkgJson[k] = value;
750
+ }
751
+ });
752
+ }
753
+
754
+ /**
755
+ * iterate through all the files that have been built, creating an exports map
756
+ */
757
+ function buildExports () {
758
+ const buildDir = path.join(process.cwd(), 'build');
759
+
760
+ witeJson(path.join(buildDir, 'cjs/package.json'), { type: 'commonjs' });
761
+ tweakCjsPaths();
762
+
763
+ const pkgPath = path.join(buildDir, 'package.json');
764
+
765
+ /** @type {PkgJson} */
766
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
767
+ const listRoot = findFiles(buildDir, '', ['cjs', 'README.md', 'LICENSE']);
768
+
769
+ if (!listRoot.some(([key]) => key === '.')) {
770
+ const indexDef = relativePath(pkg.main).replace('.js', '.d.ts');
771
+
772
+ // for the env-specifics, add a root key (if not available)
773
+ listRoot.push(['.', {
774
+ types: indexDef,
775
+ // eslint-disable-next-line sort-keys
776
+ 'react-native': createMapEntry(buildDir, pkg['react-native'], true)[1],
777
+ // eslint-disable-next-line sort-keys
778
+ browser: createMapEntry(buildDir, pkg.browser, true)[1],
779
+ node: createMapEntry(buildDir, pkg.main, true)[1]
780
+ }]);
781
+ }
782
+
783
+ // cleanup extraneous fields
784
+ delete pkg.devDependencies;
785
+
786
+ if (!pkg.main && fs.existsSync(path.join(buildDir, 'index.d.ts'))) {
787
+ pkg.main = 'index.js';
788
+ }
789
+
790
+ if (pkg.main) {
791
+ const main = pkg.main.startsWith('./')
792
+ ? pkg.main
793
+ : `./${pkg.main}`;
794
+
795
+ pkg.main = main.replace(/^\.\//, './cjs/');
796
+ pkg.module = main;
797
+ pkg.types = main.replace('.js', '.d.ts');
798
+ }
799
+
800
+ // Ensure the top-level entries always points to the CJS version
801
+ (/** @type {const} */ (['browser', 'react-native'])).forEach((k) => {
802
+ const value = pkg[k];
803
+
804
+ if (typeof value === 'string') {
805
+ const entry = value.startsWith('./')
806
+ ? value
807
+ : `./${value}`;
808
+
809
+ pkg[k] = entry.replace(/^\.\//, './cjs/');
810
+ }
811
+ });
812
+
813
+ if (Array.isArray(pkg.sideEffects)) {
814
+ pkg.sideEffects = pkg.sideEffects.map((s) =>
815
+ s.endsWith('.cjs')
816
+ ? s.replace(/^\.\//, './cjs/').replace('.cjs', '.js')
817
+ : s
818
+ );
819
+ }
820
+
821
+ pkg.type = 'module';
822
+
823
+ pkg.exports = listRoot
824
+ .filter(([path, config]) =>
825
+ // skip d.ts files, except globals.d.ts which is needed for dev-test
826
+ (path.startsWith('./globals.d.ts') || !path.endsWith('.d.ts')) &&
827
+ // we handle the CJS path at the root below
828
+ path !== './cjs/package.json' &&
829
+ // we don't export ./deno/* paths (e.g. wasm)
830
+ !path.startsWith('./deno/') &&
831
+ // others
832
+ (
833
+ typeof config === 'object' ||
834
+ !listRoot.some(([, c]) =>
835
+ typeof c === 'object' &&
836
+ Object.values(c).some((v) => v === path)
837
+ )
838
+ )
839
+ )
840
+ .sort((a, b) => a[0].localeCompare(b[0]))
841
+ .reduce((all, [path, config]) => {
842
+ const entry = typeof config === 'string'
843
+ ? config
844
+ // We need to force the types entry to the top,
845
+ // so we merge, sort and re-assemble
846
+ : Object
847
+ .entries({
848
+ ...(pkg.exports?.[path] ?? {}),
849
+ ...config
850
+ })
851
+ .sort(([a], [b]) =>
852
+ // types (first), module (first-ish), default (last)
853
+ a === 'types'
854
+ ? -1
855
+ : b === 'types'
856
+ ? 1
857
+ : a === 'module'
858
+ ? -1
859
+ : b === 'module'
860
+ ? 1
861
+ : a === 'default'
862
+ ? 1
863
+ : b === 'default'
864
+ ? -1
865
+ : 0
866
+ )
867
+ .reduce((all, [key, value]) => ({
868
+ ...all,
869
+ [key]: value
870
+ }), {});
871
+
872
+ const pathParts = path.split(/[\\/]/);
873
+
874
+ return {
875
+ ...all,
876
+ ...(
877
+ path === '.'
878
+ // eslint-disable-next-line sort-keys
879
+ ? { './cjs/package.json': './cjs/package.json', './cjs/*': './cjs/*.js' }
880
+ : ['./packageInfo', './shim'].includes(path)
881
+ ? { [`${path}.js`]: entry }
882
+ : {}
883
+ ),
884
+ [path]: entry,
885
+ ...(
886
+ path.endsWith('.mjs') || path.endsWith('.cjs')
887
+ ? { [path.replace(/\.[cm]js$/, '')]: entry }
888
+ : {}
889
+ ),
890
+ ...(
891
+ ['index.cjs', 'index.mjs'].includes(pathParts[pathParts.length - 1])
892
+ ? { [pathParts.slice(0, -1).join('/')]: entry }
893
+ : {}
894
+ )
895
+ };
896
+ }, {});
897
+
898
+ moveFields(pkg, ['main', 'module', 'browser', 'deno', 'react-native', 'types', 'exports', 'dependencies', 'optionalDependencies', 'peerDependencies', 'denoDependencies']);
899
+ witeJson(pkgPath, pkg);
900
+ }
901
+
902
+ /**
903
+ * Sorts a JSON file (typically package.json) by key
904
+ *
905
+ * @param {Record<string, unknown>} json
906
+ * @returns {Record<string, unknown>}
907
+ */
908
+ function sortJson (json) {
909
+ return Object
910
+ .entries(json)
911
+ .sort(([a], [b]) => a.localeCompare(b))
912
+ .reduce((all, [k, v]) => ({ ...all, [k]: v }), {});
913
+ }
914
+
915
+ /**
916
+ * @internal
917
+ *
918
+ * Adjusts the engine setting, highest of current and requested
919
+ *
920
+ * @param {string} [currVer]
921
+ * @returns {string}
922
+ */
923
+ function getEnginesVer (currVer) {
924
+ return currVer && engineVersionCmp(currVer, TARGET_NODE) === 1
925
+ ? currVer
926
+ : TARGET_NODE;
927
+ }
928
+
929
+ /**
930
+ * @param {string} repoPath
931
+ * @param {string | null} dir
932
+ * @param {PkgJson} json
933
+ */
934
+ function orderPackageJson (repoPath, dir, json) {
935
+ json.bugs = `https://github.com/${repoPath}/issues`;
936
+ json.homepage = `https://github.com/${repoPath}${dir ? `/tree/master/packages/${dir}` : ''}#readme`;
937
+ json.license = !json.license || json.license === 'Apache-2'
938
+ ? 'Apache-2.0'
939
+ : json.license;
940
+ json.repository = {
941
+ ...(dir
942
+ ? { directory: `packages/${dir}` }
943
+ : {}
944
+ ),
945
+ type: 'git',
946
+ url: `https://github.com/${repoPath}.git`
947
+ };
948
+ json.sideEffects = json.sideEffects || false;
949
+ json.engines = {
950
+ node: getEnginesVer(json.engines?.node)
951
+ };
952
+
953
+ // sort the object
954
+ const sorted = sortJson(json);
955
+
956
+ // remove fields we don't want to publish (may be re-added at some point)
957
+ ['contributors', 'engine-strict', 'maintainers'].forEach((d) => {
958
+ delete sorted[d];
959
+ });
960
+
961
+ // move the different entry points to the (almost) end
962
+ (/** @type {const} */ (['browser', 'deno', 'electron', 'main', 'module', 'react-native'])).forEach((d) => {
963
+ delete sorted[d];
964
+
965
+ if (json[d]) {
966
+ sorted[d] = json[d];
967
+ }
968
+ });
969
+
970
+ // move bin, scripts & dependencies to the end
971
+ (/** @type {const} */ (['bin', 'scripts', 'exports', 'dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies', 'denoDependencies', 'resolutions'])).forEach((d) => {
972
+ delete sorted[d];
973
+
974
+ const value = json[d];
975
+
976
+ if (value && Object.keys(value).length) {
977
+ sorted[d] = sortJson(value);
978
+ }
979
+ });
980
+
981
+ witeJson(path.join(process.cwd(), 'package.json'), sorted);
982
+ }
983
+
984
+ /**
985
+ * @param {string} full
986
+ * @param {string} line
987
+ * @param {number} lineNumber
988
+ * @param {string} error
989
+ * @returns {string}
990
+ */
991
+ function createError (full, line, lineNumber, error) {
992
+ return `${full}:: ${lineNumber >= 0 ? `line ${lineNumber + 1}:: ` : ''}${error}:: \n\n\t${line}\n`;
993
+ }
994
+
995
+ /**
996
+ * @param {string[]} errors
997
+ */
998
+ function throwOnErrors (errors) {
999
+ if (errors.length) {
1000
+ exitFatal(errors.join('\n'));
1001
+ }
1002
+ }
1003
+
1004
+ /**
1005
+ * @param {string[]} exts
1006
+ * @param {string} dir
1007
+ * @param {string} sub
1008
+ * @param {(path: string, line: string, lineNumber: number) => string | null | undefined} fn
1009
+ * @param {boolean} [allowComments]
1010
+ * @returns {string[]}
1011
+ */
1012
+ function loopFiles (exts, dir, sub, fn, allowComments = false) {
1013
+ return fs
1014
+ .readdirSync(sub)
1015
+ .reduce((/** @type {string[]} */ errors, inner) => {
1016
+ const full = path.join(sub, inner);
1017
+
1018
+ if (fs.statSync(full).isDirectory()) {
1019
+ return errors.concat(loopFiles(exts, dir, full, fn, allowComments));
1020
+ } else if (exts.some((e) => full.endsWith(e))) {
1021
+ fs
1022
+ .readFileSync(full, 'utf-8')
1023
+ .split('\n')
1024
+ .forEach((l, n) => {
1025
+ const t = l
1026
+ // no leading/trailing whitespace
1027
+ .trim()
1028
+ // anything starting with * (multi-line comments)
1029
+ .replace(/^\*.*/, '')
1030
+ // anything between /* ... */
1031
+ .replace(/\/\*.*\*\//g, '')
1032
+ // single line comments with // ...
1033
+ .replace(allowComments ? /--------------------/ : /\/\/.*/, '');
1034
+ const r = fn(`${dir}/${full}`, t, n);
1035
+
1036
+ if (r) {
1037
+ errors.push(r);
1038
+ }
1039
+ });
1040
+ }
1041
+
1042
+ return errors;
1043
+ }, []);
1044
+ }
1045
+
1046
+ /**
1047
+ * @param {string} dir
1048
+ */
1049
+ function lintOutput (dir) {
1050
+ throwOnErrors(
1051
+ loopFiles(['.d.ts', '.js', '.cjs'], dir, 'build', (full, l, n) => {
1052
+ if ((l.includes('import(') || (l.startsWith('import ') && l.includes(" from '"))) && l.includes('/src/')) {
1053
+ // we are not allowed to import from /src/
1054
+ return createError(full, l, n, 'Invalid import from /src/');
1055
+ // eslint-disable-next-line no-useless-escape
1056
+ } else if (/[\+\-\*\/\=\<\>\|\&\%\^\(\)\{\}\[\] ][0-9]{1,}n/.test(l)) {
1057
+ if (l.includes(';base64,')) {
1058
+ // ignore base64 encoding, e.g. data uris
1059
+ } else if (dir !== 'dev') {
1060
+ // we don't want untamed BigInt literals
1061
+ return createError(full, l, n, 'Prefer BigInt(<digits>) to <digits>n');
1062
+ }
1063
+ }
1064
+
1065
+ return null;
1066
+ })
1067
+ );
1068
+ }
1069
+
1070
+ /**
1071
+ * @param {string} dir
1072
+ */
1073
+ function lintInput (dir) {
1074
+ throwOnErrors(
1075
+ loopFiles(['.ts', '.tsx'], dir, 'src', (full, l, n) => {
1076
+ // Sadly, we have people copying and just changing all the headers without
1077
+ // giving attribution - we certainly like forks, contributions, building on
1078
+ // stuff, but doing this type of rebrand is not cool
1079
+ //
1080
+ // This does have negative effects - proper forks that add their own source
1081
+ // file will also be caught in the net, i.e. we expect all files to conform
1082
+ if (n === 0 && (
1083
+ !/\/\/ Copyright .* @polkadot\//.test(l) &&
1084
+ !/\/\/ Auto-generated via `/.test(l) &&
1085
+ !/#!\/usr\/bin\/env node/.test(l)
1086
+ )) {
1087
+ return createError(full, l, n, 'Invalid header definition');
1088
+ }
1089
+
1090
+ return null;
1091
+ }, true)
1092
+ );
1093
+ }
1094
+
1095
+ /**
1096
+ * @param {string} config
1097
+ * @returns {[string[], boolean, string[]]}
1098
+ */
1099
+ function getReferences (config) {
1100
+ const configPath = path.join(process.cwd(), config);
1101
+
1102
+ if (fs.existsSync(configPath)) {
1103
+ try {
1104
+ // We use the JSON5 parser here since we may have comments
1105
+ // (as allowed, per spec) in the actual tsconfig files
1106
+ /** @type {{ references: { path: string }[] }} */
1107
+ const tsconfig = JSON5.parse(fs.readFileSync(configPath, 'utf-8'));
1108
+ const paths = tsconfig.references.map(({ path }) => path);
1109
+
1110
+ return [
1111
+ paths.map((path) =>
1112
+ path
1113
+ .replace('../', '')
1114
+ .replace('/tsconfig.build.json', '')
1115
+ ),
1116
+ true,
1117
+ paths
1118
+ ];
1119
+ } catch (error) {
1120
+ console.error(`Unable to parse ${configPath}`);
1121
+
1122
+ throw error;
1123
+ }
1124
+ }
1125
+
1126
+ return [[], false, []];
1127
+ }
1128
+
1129
+ /**
1130
+ *
1131
+ * @param {CompileType} compileType
1132
+ * @param {string} dir
1133
+ * @param {[string, string][]} locals
1134
+ * @returns
1135
+ */
1136
+ function lintDependencies (compileType, dir, locals) {
1137
+ const { dependencies = {}, devDependencies = {}, name, optionalDependencies = {}, peerDependencies = {}, private: isPrivate } = JSON.parse(fs.readFileSync(path.join(process.cwd(), './package.json'), 'utf-8'));
1138
+
1139
+ if (isPrivate) {
1140
+ return;
1141
+ }
1142
+
1143
+ const checkDep = compileType === 'babel'
1144
+ ? '@babel/runtime'
1145
+ : compileType === 'swc'
1146
+ ? '@swc/helpers'
1147
+ : compileType === 'esbuild'
1148
+ ? null
1149
+ : 'tslib';
1150
+
1151
+ if (checkDep && !dependencies[checkDep]) {
1152
+ throw new Error(`${name} does not include the ${checkDep} dependency`);
1153
+ }
1154
+
1155
+ const deps = [
1156
+ ...Object.keys(dependencies),
1157
+ ...Object.keys(peerDependencies),
1158
+ ...Object.keys(optionalDependencies)
1159
+ ];
1160
+ const devDeps = [
1161
+ ...Object.keys(devDependencies),
1162
+ ...deps
1163
+ ];
1164
+ const [references] = getReferences('tsconfig.build.json');
1165
+ const [devRefs, hasDevConfig] = getReferences('tsconfig.spec.json');
1166
+
1167
+ /** @type {string[]} */
1168
+ const refsFound = [];
1169
+
1170
+ throwOnErrors(
1171
+ loopFiles(['.ts', '.tsx'], dir, 'src', (full, l, n) => {
1172
+ if (l.startsWith("import '") || (l.startsWith('import ') && l.includes(" from '"))) {
1173
+ const dep = l
1174
+ .split(
1175
+ l.includes(" from '")
1176
+ ? " from '"
1177
+ : " '"
1178
+ )[1]
1179
+ .split("'")[0]
1180
+ .split('/')
1181
+ .slice(0, 2)
1182
+ .join('/');
1183
+
1184
+ if (name !== dep && !dep.startsWith('.') && !IGNORE_IMPORTS.includes(dep)) {
1185
+ const local = locals.find(([, name]) => name === dep);
1186
+ const isTest = full.endsWith('.spec.ts') || full.endsWith('.test.ts') || full.endsWith('.manual.ts') || full.includes('/test/');
1187
+
1188
+ if (!(isTest ? devDeps : deps).includes(dep) && !deps.includes(dep.split('/')[0])) {
1189
+ return createError(full, l, n, `${dep} is not included in package.json dependencies`);
1190
+ } else if (local) {
1191
+ const ref = local[0];
1192
+
1193
+ if (!(isTest && hasDevConfig ? devRefs : references).includes(ref)) {
1194
+ return createError(full, l, n, `../${ref} not included in ${(isTest && hasDevConfig ? 'tsconfig.spec.json' : 'tsconfig.build.json')} references`);
1195
+ }
1196
+
1197
+ if (!refsFound.includes(ref)) {
1198
+ refsFound.push(ref);
1199
+ }
1200
+ }
1201
+ }
1202
+ }
1203
+
1204
+ return null;
1205
+ })
1206
+ );
1207
+
1208
+ const extraRefs = references.filter((r) => !refsFound.includes(r));
1209
+
1210
+ if (extraRefs.length) {
1211
+ throwOnErrors([
1212
+ createError(`${dir}/tsconfig.build.json`, extraRefs.join(', '), -1, 'Unused tsconfig.build.json references found')
1213
+ ]);
1214
+ }
1215
+ }
1216
+
1217
+ /**
1218
+ * @param {string} label
1219
+ * @param {() => unknown} fn
1220
+ */
1221
+ async function timeIt (label, fn) {
1222
+ const start = Date.now();
1223
+
1224
+ await Promise.resolve(fn());
1225
+
1226
+ console.log(`${label} (${Date.now() - start}ms)`);
1227
+ }
1228
+
1229
+ /**
1230
+ * @param {string} filepath
1231
+ * @param {boolean} withDetectImport
1232
+ * @returns {[string[], string[]]}
1233
+ */
1234
+ function extractPackageInfoImports (filepath, withDetectImport) {
1235
+ /** @type {string[]} */
1236
+ const otherImports = withDetectImport
1237
+ ? ["import { detectPackage } from '@pezkuwi/util';"]
1238
+ : [];
1239
+ /** @type {string[]} */
1240
+ const otherNames = [];
1241
+
1242
+ fs
1243
+ .readFileSync(filepath, { encoding: 'utf-8' })
1244
+ .split('\n')
1245
+ .forEach((l) => {
1246
+ const match = l.match(/import \{ packageInfo as (.*) \}/);
1247
+
1248
+ if (match) {
1249
+ otherImports.push(l);
1250
+ otherNames.push(match[1]);
1251
+ }
1252
+ });
1253
+
1254
+ otherImports.sort((a, b) => {
1255
+ const am = a.match(/\} from '(.*)';/);
1256
+ const bm = b.match(/\} from '(.*)';/);
1257
+
1258
+ if (!am) {
1259
+ throw new Error(`Unable to extract from import from ${a}`);
1260
+ } else if (!bm) {
1261
+ throw new Error(`Unable to extract from import from ${b}`);
1262
+ }
1263
+
1264
+ const af = am[1];
1265
+ const bf = bm[1];
1266
+ const pc = af.split('/')[1].localeCompare(bf.split('/')[1]);
1267
+
1268
+ return pc || af.localeCompare(bf);
1269
+ });
1270
+
1271
+ return [otherImports, otherNames];
1272
+ }
1273
+
1274
+ /**
1275
+ * @param {CompileType} compileType
1276
+ * @param {string} repoPath
1277
+ * @param {string} dir
1278
+ * @param {[string, string][]} locals
1279
+ * @returns {Promise<void>}
1280
+ */
1281
+ async function buildJs (compileType, repoPath, dir, locals) {
1282
+ const pkgJson = JSON.parse(fs.readFileSync(path.join(process.cwd(), './package.json'), 'utf-8'));
1283
+ const { name, version } = pkgJson;
1284
+
1285
+ if (!name.startsWith('@polkadot/')) {
1286
+ return;
1287
+ }
1288
+
1289
+ lintInput(dir);
1290
+
1291
+ console.log(`*** ${name} ${version}`);
1292
+
1293
+ orderPackageJson(repoPath, dir, pkgJson);
1294
+
1295
+ // move the tsc-generated *.d.ts build files to build-tsc
1296
+ if (fs.existsSync('build')) {
1297
+ copyDirSync('build', 'build-tsc', ['.d.ts']);
1298
+ }
1299
+
1300
+ if (!fs.existsSync(path.join(process.cwd(), '.skip-build'))) {
1301
+ const srcHeader = `// Copyright 2017-${new Date().getFullYear()} ${name} authors & contributors\n// SPDX-License-Identifier: Apache-2.0\n`;
1302
+ const genHeader = `${srcHeader}\n// Do not edit, auto-generated by @polkadot/dev\n`;
1303
+
1304
+ fs.writeFileSync(path.join(process.cwd(), 'src/packageInfo.ts'), `${genHeader}\nexport const packageInfo = { name: '${name}', path: 'auto', type: 'auto', version: '${version}' };\n`);
1305
+
1306
+ if (!name.startsWith('@polkadot/x-')) {
1307
+ if (!name.startsWith('@polkadot/dev')) {
1308
+ const detectOld = path.join(process.cwd(), 'src/detectPackage.ts');
1309
+ const detectOther = path.join(process.cwd(), 'src/detectOther.ts');
1310
+ const detectThis = path.join(process.cwd(), 'src/packageDetect.ts');
1311
+ const withDetectImport = name !== '@polkadot/util';
1312
+
1313
+ /** @type {string[]} */
1314
+ let otherImports = withDetectImport
1315
+ ? ["import { detectPackage } from '@pezkuwi/util';"]
1316
+ : [];
1317
+ /** @type {string[]} */
1318
+ let otherNames = [];
1319
+ const localImports = withDetectImport
1320
+ ? []
1321
+ : ["import { detectPackage } from './detectPackage.js';"];
1322
+
1323
+ localImports.push("import { packageInfo } from './packageInfo.js';");
1324
+
1325
+ if (fs.existsSync(detectOther)) {
1326
+ [otherImports, otherNames] = extractPackageInfoImports(detectOther, withDetectImport);
1327
+
1328
+ fs.rmSync(detectOther);
1329
+ } else if (fs.existsSync(detectThis)) {
1330
+ [otherImports, otherNames] = extractPackageInfoImports(detectThis, withDetectImport);
1331
+ }
1332
+
1333
+ if (withDetectImport) {
1334
+ // for @polkadot/util this file contains the detection logic, keep it
1335
+ fs.rmSync(detectOld, { force: true });
1336
+ }
1337
+
1338
+ fs.writeFileSync(detectThis, `${genHeader}// (packageInfo imports will be kept as-is, user-editable)\n\n${otherImports.join('\n')}\n\n${localImports.join('\n')}\n\ndetectPackage(packageInfo, null, [${otherNames.sort().join(', ')}]);\n`);
1339
+ }
1340
+
1341
+ const cjsRoot = path.join(process.cwd(), 'src/cjs');
1342
+
1343
+ if (fs.existsSync(path.join(cjsRoot, 'dirname.d.ts'))) {
1344
+ rimrafSync(cjsRoot);
1345
+ }
1346
+ }
1347
+
1348
+ if (fs.existsSync(path.join(process.cwd(), 'public'))) {
1349
+ buildWebpack();
1350
+ } else {
1351
+ await compileJs(compileType, 'cjs');
1352
+ await compileJs(compileType, 'esm');
1353
+
1354
+ // Deno
1355
+ await timeIt('Successfully compiled deno', () => {
1356
+ buildDeno();
1357
+ });
1358
+
1359
+ await timeIt('Successfully rewrote imports', () => {
1360
+ // adjust the import paths (deno imports from .ts - can remove this on typescript 5)
1361
+ rewriteImports('build-deno', process.cwd(), pkgJson, adjustDenoPath);
1362
+
1363
+ // adjust all output js esm to have .js path imports
1364
+ ['cjs', 'esm'].forEach((jsType) =>
1365
+ rewriteImports(`build-${compileType}-${jsType}`, process.cwd(), pkgJson, adjustJsPath)
1366
+ );
1367
+ });
1368
+
1369
+ await timeIt('Successfully combined build', () => {
1370
+ // adjust all packageInfo.js files for the correct usage
1371
+ tweakPackageInfo(compileType);
1372
+
1373
+ // copy output files (after import rewriting)
1374
+ copyBuildFiles(compileType, dir);
1375
+ });
1376
+
1377
+ await timeIt('Successfully built exports', () => {
1378
+ // everything combined now, delete what we don't need
1379
+ deleteBuildFiles();
1380
+
1381
+ // build the package.json exports
1382
+ buildExports();
1383
+ });
1384
+
1385
+ await timeIt('Successfully linted configs', () => {
1386
+ lintOutput(dir);
1387
+ lintDependencies(compileType, dir, locals);
1388
+ });
1389
+ }
1390
+ }
1391
+
1392
+ console.log();
1393
+ }
1394
+
1395
+ /**
1396
+ * Finds any tsconfig.*.json files that are not included in the root
1397
+ * tsconfig.build.json
1398
+ */
1399
+ function findUnusedTsConfig () {
1400
+ const [,, allPaths] = getReferences('tsconfig.build.json');
1401
+ const allPkgs = fs
1402
+ .readdirSync('packages')
1403
+ .filter((dir) =>
1404
+ fs.statSync(path.join(process.cwd(), 'packages', dir)).isDirectory() &&
1405
+ fs.existsSync(path.join(process.cwd(), 'packages', dir, 'src'))
1406
+ );
1407
+ /** @type {string[]} */
1408
+ const allConfigs = [];
1409
+
1410
+ for (const pkg of allPkgs) {
1411
+ allConfigs.push(...fs
1412
+ .readdirSync(`packages/${pkg}`)
1413
+ .filter((f) =>
1414
+ f.startsWith('tsconfig.') &&
1415
+ f.endsWith('.json')
1416
+ )
1417
+ .map((f) => `./packages/${pkg}/${f}`)
1418
+ );
1419
+ }
1420
+
1421
+ const missing = allConfigs.filter((c) => !allPaths.includes(c));
1422
+
1423
+ if (missing.length) {
1424
+ throw new Error(`Not reflected in the root tsconfig.build.json: ${missing.join(', ')}`);
1425
+ }
1426
+ }
1427
+
1428
+ /**
1429
+ * Main entry point
1430
+ */
1431
+ async function main () {
1432
+ const args = process.argv.slice(2);
1433
+
1434
+ /** @type {CompileType} */
1435
+ let compileType = 'tsc';
1436
+
1437
+ for (let i = 0; i < args.length; i++) {
1438
+ if (args[i] === '--compiler') {
1439
+ const type = args[++i];
1440
+
1441
+ if (type === 'tsc') {
1442
+ compileType = type;
1443
+ } else {
1444
+ throw new Error(`Invalid --compiler ${type}`);
1445
+ }
1446
+ }
1447
+ }
1448
+
1449
+ execPm('polkadot-dev-clean-build');
1450
+
1451
+ const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), './package.json'), 'utf-8'));
1452
+
1453
+ if (pkg.scripts) {
1454
+ if (pkg.scripts['build:extra']) {
1455
+ throw new Error('Found deprecated build:extra script, use build:before or build:after instead');
1456
+ }
1457
+
1458
+ if (pkg.scripts['build:before']) {
1459
+ execPm('build:before');
1460
+ }
1461
+ }
1462
+
1463
+ const repoPath = pkg.repository.url
1464
+ .split('https://github.com/')[1]
1465
+ .split('.git')[0];
1466
+
1467
+ orderPackageJson(repoPath, null, pkg);
1468
+ execPm('polkadot-exec-tsc --build tsconfig.build.json');
1469
+
1470
+ process.chdir('packages');
1471
+
1472
+ const dirs = fs
1473
+ .readdirSync('.')
1474
+ .filter((dir) =>
1475
+ fs.statSync(dir).isDirectory() &&
1476
+ fs.existsSync(path.join(process.cwd(), dir, 'src'))
1477
+ );
1478
+
1479
+ /** @type {[string, string][]} */
1480
+ const locals = [];
1481
+
1482
+ // get all package names
1483
+ for (const dir of dirs) {
1484
+ const { name } = JSON.parse(fs.readFileSync(path.join(process.cwd(), dir, './package.json'), 'utf-8'));
1485
+
1486
+ if (name.startsWith('@polkadot/')) {
1487
+ locals.push([dir, name]);
1488
+ }
1489
+ }
1490
+
1491
+ // build packages
1492
+ for (const dir of dirs) {
1493
+ process.chdir(dir);
1494
+
1495
+ await buildJs(compileType, repoPath, dir, locals);
1496
+
1497
+ process.chdir('..');
1498
+ }
1499
+
1500
+ process.chdir('..');
1501
+
1502
+ findUnusedTsConfig();
1503
+
1504
+ if (RL_CONFIGS.some((c) => fs.existsSync(path.join(process.cwd(), c)))) {
1505
+ execPm('polkadot-exec-rollup --config');
1506
+ }
1507
+
1508
+ if (pkg.scripts) {
1509
+ if (pkg.scripts['build:after']) {
1510
+ execPm('build:after');
1511
+ }
1512
+ }
1513
+ }
1514
+
1515
+ main().catch((error) => {
1516
+ console.error(error);
1517
+ process.exit(-1);
1518
+ });