Skip to content

Commit

Permalink
refactor: esm directory (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
privatenumber committed May 1, 2024
1 parent 4b1f03c commit d82d7f0
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 104 deletions.
1 change: 1 addition & 0 deletions src/esm/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { register } from './register.js';
4 changes: 2 additions & 2 deletions src/esm/register.ts → src/esm/api/register.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import module from 'node:module';
import { installSourceMapSupport } from '../source-map.js';
import { installSourceMapSupport } from '../../source-map.js';

export const registerLoader = () => {
export const register = () => {
installSourceMapSupport();

module.register(
Expand Down
19 changes: 19 additions & 0 deletions src/esm/hook/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { GlobalPreloadHook, InitializeHook } from 'module';

export const initialize: InitializeHook = async (data) => {
if (!data) {
throw new Error('tsx must be loaded with --import instead of --loader\nThe --loader flag was deprecated in Node v20.6.0 and v18.19.0');
}
};

/**
* Technically globalPreload is deprecated so it should be in loaders-deprecated
* but it shares a closure with the new load hook
*/
export const globalPreload: GlobalPreloadHook = () => `
const require = getBuiltin('module').createRequire("${import.meta.url}");
require('../source-map.cjs').installSourceMapSupport();
`;

export { load } from './load.js';
export { resolve } from './resolve.js';
84 changes: 84 additions & 0 deletions src/esm/hook/load.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { fileURLToPath } from 'url';
import type { LoadHook } from 'module';
import type { TransformOptions } from 'esbuild';
import { transform } from '../../utils/transform/index.js';
import { transformDynamicImport } from '../../utils/transform/transform-dynamic-import.js';
import { installSourceMapSupport } from '../../source-map.js';
import { isFeatureSupported, importAttributes } from '../../utils/node-features.js';
import { parent } from '../../utils/ipc/client.js';
import {
fileMatcher,
tsExtensionsPattern,
isJsonPattern,
} from './utils.js';

const applySourceMap = installSourceMapSupport();

const contextAttributesProperty = (
isFeatureSupported(importAttributes)
? 'importAttributes'
: 'importAssertions'
);

export const load: LoadHook = async (
url,
context,
defaultLoad,
) => {
/*
Filter out node:*
Maybe only handle files that start with file://
*/
if (parent.send) {
parent.send({
type: 'dependency',
path: url,
});
}

if (isJsonPattern.test(url)) {
if (!context[contextAttributesProperty]) {
context[contextAttributesProperty] = {};
}

context[contextAttributesProperty]!.type = 'json';
}

const loaded = await defaultLoad(url, context);

// CommonJS and Internal modules (e.g. node:*)
if (!loaded.source) {
return loaded;
}

const filePath = url.startsWith('file://') ? fileURLToPath(url) : url;
const code = loaded.source.toString();

if (
// Support named imports in JSON modules
loaded.format === 'json'
|| tsExtensionsPattern.test(url)
) {
const transformed = await transform(
code,
filePath,
{
tsconfigRaw: fileMatcher?.(filePath) as TransformOptions['tsconfigRaw'],
},
);

return {
format: 'module',
source: applySourceMap(transformed),
};
}

if (loaded.format === 'module') {
const dynamicImportTransformed = transformDynamicImport(filePath, code);
if (dynamicImportTransformed) {
loaded.source = applySourceMap(dynamicImportTransformed);
}
}

return loaded;
};
File renamed without changes.
104 changes: 5 additions & 99 deletions src/esm/loaders.ts → src/esm/hook/resolve.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
import path from 'path';
import { pathToFileURL, fileURLToPath } from 'url';
import { pathToFileURL } from 'url';
import type {
ResolveFnOutput, ResolveHookContext, LoadHook, GlobalPreloadHook, InitializeHook,
ResolveFnOutput, ResolveHookContext,
} from 'module';
import type { TransformOptions } from 'esbuild';
import { transform } from '../utils/transform/index.js';
import { transformDynamicImport } from '../utils/transform/transform-dynamic-import.js';
import { resolveTsPath } from '../utils/resolve-ts-path.js';
import { installSourceMapSupport } from '../source-map.js';
import { isFeatureSupported, importAttributes } from '../utils/node-features.js';
import { parent } from '../utils/ipc/client.js';
import type { NodeError } from '../types.js';
import { isRelativePathPattern } from '../utils/is-relative-path-pattern.js';
import { resolveTsPath } from '../../utils/resolve-ts-path.js';
import type { NodeError } from '../../types.js';
import { isRelativePathPattern } from '../../utils/is-relative-path-pattern.js';
import {
tsconfigPathsMatcher,
fileMatcher,
tsExtensionsPattern,
isJsonPattern,
getFormatFromFileUrl,
fileProtocol,
allowJs,
type MaybePromise,
} from './utils.js';

const applySourceMap = installSourceMapSupport();

const isDirectoryPattern = /\/(?:$|\?)/;

type NextResolve = (
Expand All @@ -39,21 +29,6 @@ type resolve = (
recursiveCall?: boolean,
) => MaybePromise<ResolveFnOutput>;

export const initialize: InitializeHook = async (data) => {
if (!data) {
throw new Error('tsx must be loaded with --import instead of --loader\nThe --loader flag was deprecated in Node v20.6.0 and v18.19.0');
}
};

/**
* Technically globalPreload is deprecated so it should be in loaders-deprecated
* but it shares a closure with the new load hook
*/
export const globalPreload: GlobalPreloadHook = () => `
const require = getBuiltin('module').createRequire("${import.meta.url}");
require('../source-map.cjs').installSourceMapSupport();
`;

const resolveExplicitPath = async (
defaultResolve: NextResolve,
specifier: string,
Expand Down Expand Up @@ -218,72 +193,3 @@ export const resolve: resolve = async (
throw error;
}
};

const contextAttributesProperty = (
isFeatureSupported(importAttributes)
? 'importAttributes'
: 'importAssertions'
);

export const load: LoadHook = async (
url,
context,
defaultLoad,
) => {
/*
Filter out node:*
Maybe only handle files that start with file://
*/
if (parent.send) {
parent.send({
type: 'dependency',
path: url,
});
}

if (isJsonPattern.test(url)) {
if (!context[contextAttributesProperty]) {
context[contextAttributesProperty] = {};
}

context[contextAttributesProperty]!.type = 'json';
}

const loaded = await defaultLoad(url, context);

// CommonJS and Internal modules (e.g. node:*)
if (!loaded.source) {
return loaded;
}

const filePath = url.startsWith('file://') ? fileURLToPath(url) : url;
const code = loaded.source.toString();

if (
// Support named imports in JSON modules
loaded.format === 'json'
|| tsExtensionsPattern.test(url)
) {
const transformed = await transform(
code,
filePath,
{
tsconfigRaw: fileMatcher?.(filePath) as TransformOptions['tsconfigRaw'],
},
);

return {
format: 'module',
source: applySourceMap(transformed),
};
}

if (loaded.format === 'module') {
const dynamicImportTransformed = transformDynamicImport(filePath, code);
if (dynamicImportTransformed) {
loaded.source = applySourceMap(dynamicImportTransformed);
}
}

return loaded;
};
File renamed without changes.
6 changes: 3 additions & 3 deletions src/esm/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { isMainThread } from 'node:worker_threads';
import { isFeatureSupported, moduleRegister } from '../utils/node-features.js';
import { registerLoader } from './register.js';
import { register } from './api/index.js';

// Loaded via --import flag
if (
isFeatureSupported(moduleRegister)
&& isMainThread
) {
registerLoader();
register();
}

export * from './loaders.js';
export * from './hook/index.js';

0 comments on commit d82d7f0

Please sign in to comment.