mirror of
https://github.com/pezkuwichain/pezkuwi-dev.git
synced 2026-04-22 05:38:01 +00:00
Initial rebrand: @polkadot -> @pezkuwi (3 packages)
- Package namespace: @polkadot/dev -> @pezkuwi/dev - Repository: polkadot-js/dev -> pezkuwichain/pezkuwi-dev - Author: Pezkuwi Team <team@pezkuwichain.io> Packages: - @pezkuwi/dev (build tools, linting, CI scripts) - @pezkuwi/dev-test (test runner) - @pezkuwi/dev-ts (TypeScript build) Upstream: polkadot-js/dev v0.83.3
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
// Copyright 2017-2025 @polkadot/dev-ts authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { loaderOptions } from './common.js';
|
||||
|
||||
loaderOptions.isCached = true;
|
||||
|
||||
export * from './index.js';
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright 2017-2025 @polkadot/dev-ts authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { LoaderOptions } from './types.js';
|
||||
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
|
||||
/** The path we are being executed from */
|
||||
export const CWD_PATH = process.cwd();
|
||||
|
||||
/** The cwd path we are being executed from in URL form */
|
||||
export const CWD_URL = pathToFileURL(`${CWD_PATH}/`);
|
||||
|
||||
/** The root path to node_modules (assuming it is in the root) */
|
||||
export const MOD_PATH = path.join(CWD_PATH, 'node_modules');
|
||||
|
||||
/** List of allowed extensions for mappings */
|
||||
export const EXT_TS_ARRAY = ['.ts', '.tsx'];
|
||||
|
||||
/** RegEx for files that we support via this loader */
|
||||
export const EXT_TS_REGEX = /\.tsx?$/;
|
||||
|
||||
/** RegEx for matching JS files (imports map to TS) */
|
||||
export const EXT_JS_REGEX = /\.jsx?$/;
|
||||
|
||||
/** RegEx for json files (as actually aliassed in polkadot-js) */
|
||||
export const EXT_JSON_REGEX = /\.json$/;
|
||||
|
||||
/** Options for loader config */
|
||||
export const loaderOptions: LoaderOptions = {};
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2017-2025 @polkadot/dev-ts authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Adapted from: https://nodejs.org/api/esm.html#esm_transpiler_loader
|
||||
//
|
||||
// NOTE: This assumes the loader implementation for Node.js >= 18
|
||||
|
||||
import { loaderOptions } from './common.js';
|
||||
|
||||
loaderOptions.isCached = new URL(import.meta.url).searchParams.get('isCached') === 'true';
|
||||
|
||||
export { load } from './loader.js';
|
||||
export { resolve } from './resolver.js';
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright 2017-2025 @polkadot/dev-ts authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import crypto from 'node:crypto';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import ts from 'typescript';
|
||||
|
||||
import { EXT_TS_REGEX, loaderOptions } from './common.js';
|
||||
|
||||
interface Loaded {
|
||||
format: 'commonjs' | 'module';
|
||||
shortCircuit?: boolean;
|
||||
source: string;
|
||||
}
|
||||
|
||||
type NexLoad = (url: string, context: Record<string, unknown>) => Promise<Loaded>;
|
||||
|
||||
/**
|
||||
* Load all TypeScript files, compile via tsc on-the-fly
|
||||
**/
|
||||
export async function load (url: string, context: Record<string, unknown>, nextLoad: NexLoad): Promise<Loaded> {
|
||||
if (EXT_TS_REGEX.test(url)) {
|
||||
// used the chained loaders to retrieve
|
||||
const { source } = await nextLoad(url, {
|
||||
...context,
|
||||
format: 'module'
|
||||
});
|
||||
|
||||
// we use a hash of the source to determine caching
|
||||
const sourceHash = `//# sourceHash=${crypto.createHash('sha256').update(source).digest('hex')}`;
|
||||
const compiledFile = url.includes('/src/')
|
||||
? fileURLToPath(
|
||||
url
|
||||
.replace(/\.tsx?$/, '.js')
|
||||
.replace('/src/', '/build-loader/')
|
||||
)
|
||||
: null;
|
||||
|
||||
if (loaderOptions.isCached && compiledFile && fs.existsSync(compiledFile)) {
|
||||
const compiled = fs.readFileSync(compiledFile, 'utf-8');
|
||||
|
||||
if (compiled.includes(sourceHash)) {
|
||||
return {
|
||||
format: 'module',
|
||||
source: compiled
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// compile via typescript
|
||||
const { outputText } = ts.transpileModule(source.toString(), {
|
||||
compilerOptions: {
|
||||
...(
|
||||
url.endsWith('.tsx')
|
||||
? { jsx: ts.JsxEmit.ReactJSX }
|
||||
: {}
|
||||
),
|
||||
esModuleInterop: true,
|
||||
importHelpers: true,
|
||||
inlineSourceMap: true,
|
||||
module: ts.ModuleKind.ESNext,
|
||||
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
||||
skipLibCheck: true,
|
||||
// Aligns with packages/dev/scripts/polkadot-dev-build-ts & packages/dev/config/tsconfig
|
||||
target: ts.ScriptTarget.ES2022
|
||||
},
|
||||
fileName: fileURLToPath(url)
|
||||
});
|
||||
|
||||
if (loaderOptions.isCached && compiledFile) {
|
||||
const compiledDir = path.dirname(compiledFile);
|
||||
|
||||
if (!fs.existsSync(compiledDir)) {
|
||||
fs.mkdirSync(compiledDir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.writeFileSync(compiledFile, `${outputText}\n${sourceHash}`, 'utf-8');
|
||||
}
|
||||
|
||||
return {
|
||||
format: 'module',
|
||||
source: outputText
|
||||
};
|
||||
}
|
||||
|
||||
return nextLoad(url, context);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2017-2025 @polkadot/dev-ts authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Do not edit, auto-generated by @polkadot/dev
|
||||
|
||||
export const packageInfo = { name: '@polkadot/dev-ts', path: 'auto', type: 'auto', version: '0.84.2' };
|
||||
@@ -0,0 +1,134 @@
|
||||
// Copyright 2017-2025 @polkadot/dev-ts authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@polkadot/dev-test/globals.d.ts" />
|
||||
|
||||
import path from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
|
||||
import { CWD_PATH } from './common.js';
|
||||
import { resolveAlias, resolveExtBare, resolveExtJs, resolveExtJson, resolveExtTs } from './resolver.js';
|
||||
|
||||
const ROOT_URL = pathToFileURL(`${CWD_PATH}/`);
|
||||
const SRC_PATH = 'packages/dev/src';
|
||||
const SRC_URL = pathToFileURL(`${CWD_PATH}/${SRC_PATH}/`);
|
||||
const INDEX_PATH = `${SRC_PATH}/index.ts`;
|
||||
const INDEX_URL = pathToFileURL(INDEX_PATH);
|
||||
|
||||
describe('resolveExtTs', () => {
|
||||
it('returns no value for a non .{ts, tsx} extension', () => {
|
||||
expect(
|
||||
resolveExtTs(`./${SRC_PATH}/cjs/sample.js`, ROOT_URL)
|
||||
).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('returns a correct object for a .ts extension', () => {
|
||||
expect(
|
||||
resolveExtTs(INDEX_PATH, ROOT_URL)
|
||||
).toEqual({
|
||||
format: 'module',
|
||||
shortCircuit: true,
|
||||
url: INDEX_URL.href
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveExtJs', () => {
|
||||
const modFound = {
|
||||
format: 'module',
|
||||
shortCircuit: true,
|
||||
url: pathToFileURL(`${CWD_PATH}/${SRC_PATH}/mod.ts`).href
|
||||
};
|
||||
|
||||
it('returns the correct value for ./mod.js resolution', () => {
|
||||
expect(
|
||||
resolveExtJs('./mod.js', SRC_URL)
|
||||
).toEqual(modFound);
|
||||
});
|
||||
|
||||
it('returns the correct value for ../mod.js resolution', () => {
|
||||
expect(
|
||||
resolveExtJs('../mod.js', pathToFileURL(`${CWD_PATH}/${SRC_PATH}/rootJs/index.ts`))
|
||||
).toEqual(modFound);
|
||||
});
|
||||
|
||||
it('returns a correct object for a .jsx extension', () => {
|
||||
expect(
|
||||
resolveExtJs(`./${SRC_PATH}/rootJs/Jsx.jsx`, ROOT_URL)
|
||||
).toEqual({
|
||||
format: 'module',
|
||||
shortCircuit: true,
|
||||
url: pathToFileURL(`${SRC_PATH}/rootJs/Jsx.tsx`).href
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveExtJson', () => {
|
||||
it('resolves .json files', () => {
|
||||
expect(
|
||||
resolveExtJson('../package.json', SRC_URL)
|
||||
).toEqual({
|
||||
format: 'json',
|
||||
shortCircuit: true,
|
||||
url: pathToFileURL(path.join(SRC_PATH, '../package.json')).href
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveExtBare', () => {
|
||||
const indexFound = {
|
||||
format: 'module',
|
||||
shortCircuit: true,
|
||||
url: INDEX_URL.href
|
||||
};
|
||||
|
||||
it('does not resolve non-relative paths', () => {
|
||||
expect(
|
||||
resolveExtBare(INDEX_PATH, ROOT_URL)
|
||||
).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('resolves to the index via .', () => {
|
||||
expect(
|
||||
resolveExtBare('.', SRC_URL)
|
||||
).toEqual(indexFound);
|
||||
});
|
||||
|
||||
it('resolves to the index via ./index', () => {
|
||||
expect(
|
||||
resolveExtBare('./index', SRC_URL)
|
||||
).toEqual(indexFound);
|
||||
});
|
||||
|
||||
it('resolves to the sub-directory via ./rootJs', () => {
|
||||
expect(
|
||||
resolveExtBare('./rootJs', SRC_URL)
|
||||
).toEqual({
|
||||
format: 'module',
|
||||
shortCircuit: true,
|
||||
url: pathToFileURL(`${SRC_PATH}/rootJs/index.ts`).href
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves to extensionless path', () => {
|
||||
expect(
|
||||
resolveExtBare('./packageInfo', SRC_URL)
|
||||
).toEqual({
|
||||
format: 'module',
|
||||
shortCircuit: true,
|
||||
url: pathToFileURL(`${SRC_PATH}/packageInfo.ts`).href
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveAliases', () => {
|
||||
it('resolves packageInfo', () => {
|
||||
expect(
|
||||
resolveAlias('@polkadot/dev-ts/packageInfo', ROOT_URL)
|
||||
).toEqual({
|
||||
format: 'module',
|
||||
shortCircuit: true,
|
||||
url: pathToFileURL('packages/dev-ts/src/packageInfo.ts').href
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,224 @@
|
||||
// Copyright 2017-2025 @polkadot/dev-ts authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL, URL } from 'node:url';
|
||||
|
||||
import { CWD_URL, EXT_JS_REGEX, EXT_JSON_REGEX, EXT_TS_ARRAY, EXT_TS_REGEX } from './common.js';
|
||||
import { tsAliases } from './tsconfig.js';
|
||||
|
||||
interface Resolved {
|
||||
format: 'commonjs' | 'json' | 'module';
|
||||
shortCircuit?: boolean;
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface ResolverContext {
|
||||
parentURL?: string;
|
||||
}
|
||||
|
||||
type Resolver = (specifier: string, context: ResolverContext) => Resolved | undefined;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* From a specified URL, extract the actual full path as well as the
|
||||
* directory that this path reflects (either equivalent to path or the
|
||||
* root of the file being referenced)
|
||||
*/
|
||||
function getParentPath (parentUrl: URL | string): { parentDir: string; parentPath: string; } {
|
||||
const parentPath = fileURLToPath(parentUrl);
|
||||
|
||||
return {
|
||||
parentDir: fs.existsSync(parentPath) && fs.lstatSync(parentPath).isDirectory()
|
||||
? parentPath
|
||||
: path.dirname(parentPath),
|
||||
parentPath
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Resolve fully-specified imports with extensions.
|
||||
**/
|
||||
export function resolveExtTs (specifier: string, parentUrl: URL | string): Resolved | void {
|
||||
// handle .ts extensions directly
|
||||
if (EXT_TS_REGEX.test(specifier)) {
|
||||
return {
|
||||
format: 'module',
|
||||
shortCircuit: true,
|
||||
url: new URL(specifier, parentUrl).href
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Resolve fully-specified imports with extensions. Here we cater for the TS
|
||||
* mapping of import foo from './bar.js' where only './bar.ts' exists
|
||||
**/
|
||||
export function resolveExtJs (specifier: string, parentUrl: URL | string): Resolved | void {
|
||||
// handle ts imports where import *.js -> *.ts
|
||||
// (unlike the ts resolution, we only cater for relative paths)
|
||||
if (specifier.startsWith('.') && EXT_JS_REGEX.test(specifier)) {
|
||||
const full = fileURLToPath(new URL(specifier, parentUrl));
|
||||
|
||||
// when it doesn't exist, we try and see if a source replacement helps
|
||||
if (!fs.existsSync(full)) {
|
||||
const found = EXT_TS_ARRAY
|
||||
.map((e) => full.replace(EXT_JS_REGEX, e))
|
||||
.find((f) => fs.existsSync(f) && fs.lstatSync(f).isFile());
|
||||
|
||||
if (found) {
|
||||
return {
|
||||
format: 'module',
|
||||
shortCircuit: true,
|
||||
url: pathToFileURL(found).href
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Resolution for Json files. Generally these would be via path aliasing.
|
||||
*/
|
||||
export function resolveExtJson (specifier: string, parentUrl: URL | string): Resolved | void {
|
||||
if (specifier.startsWith('.') && EXT_JSON_REGEX.test(specifier)) {
|
||||
const { parentDir } = getParentPath(parentUrl);
|
||||
const jsonPath = path.join(parentDir, specifier);
|
||||
|
||||
if (fs.existsSync(jsonPath)) {
|
||||
return {
|
||||
// .json needs to be in 'json' format for the loader, for the
|
||||
// the rest (it should only be TS) we use the 'module' format
|
||||
format: 'json',
|
||||
shortCircuit: true,
|
||||
url: pathToFileURL(jsonPath).href
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Resolve relative (extensionless) paths.
|
||||
*
|
||||
* At some point we probably might need to extend this to cater for the
|
||||
* ts (recommended) approach for using .js extensions inside the sources.
|
||||
* However, since we don't use this in the polkadot-js code, can kick this
|
||||
* down the line
|
||||
**/
|
||||
export function resolveExtBare (specifier: string, parentUrl: URL | string): Resolved | void {
|
||||
if (specifier.startsWith('.')) {
|
||||
const { parentDir, parentPath } = getParentPath(parentUrl);
|
||||
const found = specifier === '.'
|
||||
? (
|
||||
// handle . imports for <dir>/index.ts
|
||||
EXT_TS_ARRAY
|
||||
.map((e) => path.join(parentDir, `index${e}`))
|
||||
.find((f) => fs.existsSync(f)) ||
|
||||
// handle the case where parentUrl needs an extension (generally via alias)
|
||||
EXT_TS_ARRAY
|
||||
.map((e) => `${parentPath}${e}`)
|
||||
.find((f) => fs.existsSync(f))
|
||||
)
|
||||
: (
|
||||
// tests to see if this is a file (without extension)
|
||||
EXT_TS_ARRAY
|
||||
.map((e) => path.join(parentDir, `${specifier}${e}`))
|
||||
.find((f) => fs.existsSync(f)) ||
|
||||
// test to see if this is a directory
|
||||
EXT_TS_ARRAY
|
||||
.map((e) => path.join(parentDir, `${specifier}/index${e}`))
|
||||
.find((f) => fs.existsSync(f))
|
||||
);
|
||||
|
||||
if (found) {
|
||||
return {
|
||||
format: 'module',
|
||||
shortCircuit: true,
|
||||
url: pathToFileURL(found).href
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Resolve anything that is not an alias
|
||||
**/
|
||||
export function resolveNonAlias (specifier: string, parentUrl: URL | string): Resolved | void {
|
||||
return (
|
||||
resolveExtTs(specifier, parentUrl) ||
|
||||
resolveExtJs(specifier, parentUrl) ||
|
||||
resolveExtJson(specifier, parentUrl) ||
|
||||
resolveExtBare(specifier, parentUrl)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Resolve TS alias mappings as defined in the tsconfig.json file
|
||||
**/
|
||||
export function resolveAlias (specifier: string, _parentUrl: URL | string, aliases = tsAliases): Resolved | void {
|
||||
const parts = specifier.split(/[\\/]/);
|
||||
const found = aliases
|
||||
// return a [filter, [...partIndex]] mapping
|
||||
.map((alias) => ({
|
||||
alias,
|
||||
indexes: parts
|
||||
.map((_, i) => i)
|
||||
.filter((start) =>
|
||||
(
|
||||
alias.isWildcard
|
||||
// parts should have more entries than the wildcard
|
||||
? parts.length > alias.filter.length
|
||||
// or the same amount in case of a non-wildcard match
|
||||
: parts.length === alias.filter.length
|
||||
) &&
|
||||
// match all parts of the alias
|
||||
alias.filter.every((f, i) =>
|
||||
parts[start + i] &&
|
||||
parts[start + i] === f
|
||||
)
|
||||
)
|
||||
}))
|
||||
// we only return the first
|
||||
.find(({ indexes }) => indexes.length);
|
||||
|
||||
if (found) {
|
||||
// do the actual un-aliased resolution
|
||||
return resolveNonAlias(
|
||||
`./${found.alias.path.replace('*', path.join(...parts.slice(found.alias.filter.length)))}`,
|
||||
found.alias.url
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a path using our logic.
|
||||
*
|
||||
* 1. First we attempt to directly resolve if .ts/.tsx extension is found
|
||||
* 2. Then we do relative resolves (this is for extension-less .ts files)
|
||||
* 3. The we try to do resolution via TS aliases
|
||||
*
|
||||
* ... finally, try the next loader in the chain
|
||||
*/
|
||||
export function resolve (specifier: string, context: ResolverContext, nextResolve: Resolver) {
|
||||
const parentUrl = context.parentURL || CWD_URL;
|
||||
|
||||
return (
|
||||
resolveNonAlias(specifier, parentUrl) ||
|
||||
resolveAlias(specifier, parentUrl) ||
|
||||
nextResolve(specifier, context)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2017-2025 @polkadot/dev-ts authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Adapted from: https://nodejs.org/api/esm.html#esm_transpiler_loader
|
||||
//
|
||||
// NOTE: This assumes the loader implementation for Node.js >= 18
|
||||
|
||||
import { loaderOptions } from './common.js';
|
||||
|
||||
loaderOptions.isCached = true;
|
||||
|
||||
export { resolve } from './resolver.js';
|
||||
export { load } from './testLoader.js';
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright 2017-2025 @polkadot/dev-ts authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import crypto from 'node:crypto';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import ts from 'typescript';
|
||||
|
||||
import { EXT_TS_REGEX, loaderOptions } from './common.js';
|
||||
|
||||
interface Loaded {
|
||||
format: 'commonjs' | 'module';
|
||||
shortCircuit?: boolean;
|
||||
source: string;
|
||||
}
|
||||
|
||||
type NexLoad = (url: string, context: Record<string, unknown>) => Promise<Loaded>;
|
||||
|
||||
/**
|
||||
* Load all TypeScript files, compile via tsc on-the-fly
|
||||
**/
|
||||
export async function load (url: string, context: Record<string, unknown>, nextLoad: NexLoad): Promise<Loaded> {
|
||||
if (EXT_TS_REGEX.test(url)) {
|
||||
// used the chained loaders to retrieve
|
||||
const { source } = await nextLoad(url, {
|
||||
...context,
|
||||
format: 'module'
|
||||
});
|
||||
|
||||
// This ensures there is support for Node v22 while also maintaining backwards compatibility for testing.
|
||||
const modifiedSrc = Buffer.from(source.toString().replace(/assert\s*\{\s*type:\s*'json'\s*\}/g, 'with { type: \'json\' }'), 'utf-8');
|
||||
|
||||
// we use a hash of the source to determine caching
|
||||
const sourceHash = `//# sourceHash=${crypto.createHash('sha256').update(modifiedSrc as unknown as string).digest('hex')}`;
|
||||
const compiledFile = url.includes('/src/')
|
||||
? fileURLToPath(
|
||||
url
|
||||
.replace(/\.tsx?$/, '.js')
|
||||
.replace('/src/', '/build-loader/')
|
||||
)
|
||||
: null;
|
||||
|
||||
if (loaderOptions.isCached && compiledFile && fs.existsSync(compiledFile)) {
|
||||
const compiled = fs.readFileSync(compiledFile, 'utf-8');
|
||||
|
||||
if (compiled.includes(sourceHash)) {
|
||||
return {
|
||||
format: 'module',
|
||||
source: compiled
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// compile via typescript
|
||||
const { outputText } = ts.transpileModule(modifiedSrc.toString(), {
|
||||
compilerOptions: {
|
||||
...(
|
||||
url.endsWith('.tsx')
|
||||
? { jsx: ts.JsxEmit.ReactJSX }
|
||||
: {}
|
||||
),
|
||||
esModuleInterop: true,
|
||||
importHelpers: true,
|
||||
inlineSourceMap: true,
|
||||
module: ts.ModuleKind.ESNext,
|
||||
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
||||
skipLibCheck: true,
|
||||
// Aligns with packages/dev/scripts/polkadot-dev-build-ts & packages/dev/config/tsconfig
|
||||
target: ts.ScriptTarget.ES2022
|
||||
},
|
||||
fileName: fileURLToPath(url)
|
||||
});
|
||||
|
||||
if (loaderOptions.isCached && compiledFile) {
|
||||
const compiledDir = path.dirname(compiledFile);
|
||||
|
||||
if (!fs.existsSync(compiledDir)) {
|
||||
fs.mkdirSync(compiledDir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.writeFileSync(compiledFile, `${outputText}\n${sourceHash}`, 'utf-8');
|
||||
}
|
||||
|
||||
return {
|
||||
format: 'module',
|
||||
source: outputText
|
||||
};
|
||||
}
|
||||
|
||||
return nextLoad(url, context);
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// Copyright 2017-2025 @polkadot/dev-ts authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { TsAlias } from './types.js';
|
||||
|
||||
import JSON5 from 'json5';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
|
||||
import { CWD_PATH, CWD_URL, MOD_PATH } from './common.js';
|
||||
|
||||
interface JsonConfig {
|
||||
compilerOptions?: {
|
||||
baseUrl?: string;
|
||||
paths?: Record<string, string[]>;
|
||||
};
|
||||
extends?: string | string[];
|
||||
}
|
||||
|
||||
interface PartialConfig {
|
||||
paths: Record<string, string[]>;
|
||||
url?: URL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Extracts the (relevant) tsconfig info, also using extends
|
||||
**/
|
||||
function readConfigFile (currentPath = CWD_PATH, tsconfig = 'tsconfig.json', fromFile?: string): PartialConfig {
|
||||
const configFile = path.join(currentPath, tsconfig);
|
||||
|
||||
if (!fs.existsSync(configFile)) {
|
||||
console.warn(`No ${configFile}${fromFile ? ` (extended from ${fromFile})` : ''} found, assuming defaults`);
|
||||
|
||||
return { paths: {} };
|
||||
}
|
||||
|
||||
try {
|
||||
const { compilerOptions, extends: parentConfig } = JSON5.parse<JsonConfig>(fs.readFileSync(configFile, 'utf8'));
|
||||
let url: URL | undefined;
|
||||
|
||||
if (compilerOptions?.baseUrl) {
|
||||
const configDir = path.dirname(configFile);
|
||||
|
||||
// the baseParentUrl is relative to the actual config file
|
||||
url = pathToFileURL(path.join(configDir, `${compilerOptions.baseUrl}/`));
|
||||
}
|
||||
|
||||
// empty paths if none are found
|
||||
let paths = compilerOptions?.paths || {};
|
||||
|
||||
if (parentConfig) {
|
||||
const allExtends = Array.isArray(parentConfig)
|
||||
? parentConfig
|
||||
: [parentConfig];
|
||||
|
||||
for (const extendsPath of allExtends) {
|
||||
const extRoot = extendsPath.startsWith('.')
|
||||
? currentPath
|
||||
: MOD_PATH;
|
||||
const extSubs = extendsPath.split(/[\\/]/);
|
||||
const extPath = path.join(extRoot, ...extSubs.slice(0, -1));
|
||||
const extConfig = readConfigFile(extPath, extSubs.at(-1), configFile);
|
||||
|
||||
// base configs are overridden by later configs, order here matters
|
||||
// FIXME The paths would be relative to the baseUrl at that point... for
|
||||
// now we don't care much since we define these 2 together in all @polkadot
|
||||
// configs, but it certainly _may_ create and issue at some point (for others)
|
||||
paths = { ...extConfig.paths, ...paths };
|
||||
url = url || extConfig.url;
|
||||
}
|
||||
}
|
||||
|
||||
return url
|
||||
? { paths, url }
|
||||
: { paths };
|
||||
} catch (error) {
|
||||
console.error(`FATAL: Error parsing ${configFile}:: ${(error as Error).message}`);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Retrieves all TS aliases definitions
|
||||
**/
|
||||
function extractAliases (): TsAlias[] {
|
||||
const { paths, url = CWD_URL } = readConfigFile();
|
||||
|
||||
return Object
|
||||
.entries(paths)
|
||||
.filter((kv): kv is [string, [string, ...string[]]] => !!kv[1].length)
|
||||
// TODO The path value is an array - we only handle the first entry in there,
|
||||
// this is a possible fix into the future if it is ever an issue... (may have
|
||||
// some impacts on the actual loader where only 1 alias is retrieved)
|
||||
.map(([key, [path]]) => {
|
||||
const filter = key.split(/[\\/]/);
|
||||
const isWildcard = filter.at(-1) === '*';
|
||||
|
||||
// ensure that when we have wilcards specified, they always occur in the last position
|
||||
if (filter.filter((f) => f.includes('*')).length !== (isWildcard ? 1 : 0)) {
|
||||
throw new Error(`FATAL: Wildcards in tsconfig.json path entries are only supported in the last position. Invalid ${key}: ${path} mapping`);
|
||||
}
|
||||
|
||||
return {
|
||||
filter: isWildcard
|
||||
? filter.slice(0, -1)
|
||||
: filter,
|
||||
isWildcard,
|
||||
path,
|
||||
url
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/** We only export the aliases from the config */
|
||||
export const tsAliases = extractAliases();
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2017-2025 @polkadot/dev-ts authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export interface TsAlias {
|
||||
filter: string[];
|
||||
isWildcard?: boolean;
|
||||
path: string;
|
||||
url: URL;
|
||||
}
|
||||
|
||||
export interface LoaderOptions {
|
||||
isCached?: boolean;
|
||||
}
|
||||
Reference in New Issue
Block a user