Skip to content

Commit

Permalink
feat: provide cached TS resolvers
Browse files Browse the repository at this point in the history
  • Loading branch information
ph-fritsche committed Nov 23, 2023
1 parent 385dfb2 commit 7990398
Show file tree
Hide file tree
Showing 25 changed files with 610 additions and 204 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jobs:
yarn-
- run: yarn install --frozen-lockfile
- run: yarn lint
- run: yarn setup
- run: yarn test
- uses: codecov/codecov-action@v3
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/node_modules
/coverage
/build
/dist
/.swc

Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@
},
"type": "module",
"scripts": {
"build": "scripts ts-build2 && cp src/conductor/node/experimental.cjs dist/conductor/node/experimental.cjs",
"dev": "node --require ./util/dev.cjs --require ./src/conductor/node/experimental.cjs --experimental-loader ./src/conductor/node/loader-src.js --experimental-import-meta-resolve --enable-source-maps --watch",
"test": "node --require ./src/conductor/node/experimental.cjs --experimental-loader ./src/conductor/node/loader-src.js --experimental-import-meta-resolve --enable-source-maps ./util/test.ts",
"build": "scripts ts-build2 && cp src/node/experimental.cjs dist/node/experimental.cjs && cp src/node/loader-netlocal.js dist/node/loader-netlocal.js",
"dev": "node --require ./util/devenv.cjs --require ./src/node/experimental.cjs --experimental-loader ./build/loader-src.js --experimental-import-meta-resolve --enable-source-maps --watch",
"setup": "node ./util/setup.js",
"test": "node --require ./src/node/experimental.cjs --experimental-loader ./build/loader-src.js --experimental-import-meta-resolve --enable-source-maps ./util/test.ts",
"typecheck": "tsc && tsc -p test/tsconfig.json",
"lint": "eslint ."
},
Expand Down
2 changes: 1 addition & 1 deletion src/conductor/NodeTestConductor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ if (!import.meta.resolve) {
}
const nodeFetchUrl = await import.meta.resolve('node-fetch', import.meta.url)

const loaderPath = path.dirname(url.fileURLToPath(import.meta.url)) + '/node'
const loaderPath = path.dirname(url.fileURLToPath(import.meta.url)) + '/../node'

export class NodeTestConductor extends TestConductor {
constructor(
Expand Down
64 changes: 0 additions & 64 deletions src/conductor/node/loader-src.js

This file was deleted.

128 changes: 0 additions & 128 deletions src/conductor/node/ts.js

This file was deleted.

File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import process from 'node:process'

/**
* @type {NodeJSLoader.Resolver}
* @type NodeJSLoader.Resolver
*/
export const resolve = async (specifier, context, nextResolve) => {
if (context.parentURL?.startsWith('http://127.0.0.1:')) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@

import module from 'node:module'

/**
* @type {NodeJSLoader.Resolver}
*/
export const resolve = async (specifier, context, nextResolve) => {
export const resolve: NodeJSLoader.Resolver = async (specifier, context, nextResolve) => {
const id = specifier.startsWith('node:') ? specifier.substring(5) : specifier
if (module.builtinModules.includes(id)) {
return {
Expand Down
113 changes: 113 additions & 0 deletions src/node/loader-src.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import console from 'node:console'
import fs from 'node:fs'
import fsPromise from 'node:fs/promises'
import os from 'node:os'
import path from 'node:path'
import { URL, fileURLToPath, pathToFileURL } from 'node:url'
import ts from 'typescript'
import { transform } from '@swc/core'
import { CachedFilesystem, TsConfigResolver, TsModuleResolver } from '../ts'

const PARAM_INSTRUMENT_COVERAGE_VAR = 'coverage'

const cachedFs = new CachedFilesystem({
caseSensitive: os.platform().startsWith('win'),
existsSync: s => fs.existsSync(s),
readFileSync: s => fs.readFileSync(s),
realpathSync: s => fs.realpathSync(s),
})
const moduleResolver = new TsModuleResolver(cachedFs)
const configResolver = new TsConfigResolver(cachedFs)

export const resolve: NodeJSLoader.Resolver = async (specifier, context, nextResolve) => {
if (!context.parentURL
&& specifier.startsWith('file://')
) {
return {
shortCircuit: true,
url: specifier,
}
} else if (
!/^[\w@]/.test(specifier)
&& context.parentURL?.startsWith('file://')
&& !context.parentURL.includes('/node_modules/')
) {
const parentUrl = new URL(context.parentURL)
const parentPath = fileURLToPath(parentUrl)
if (/\.[tj]sx?$/.test(parentPath)) {
const compilerOptions = configResolver.getCompilerOptions(path.dirname(parentPath))
const resolved = moduleResolver.resolveModule(
specifier,
parentPath,
compilerOptions,
ts.ModuleKind.ESNext,
)
if (resolved) {
let resolvedUrl = pathToFileURL(resolved)
const coverageVar = parentUrl.searchParams.get(PARAM_INSTRUMENT_COVERAGE_VAR)
if (coverageVar) {
resolvedUrl.searchParams.set(PARAM_INSTRUMENT_COVERAGE_VAR, coverageVar)
}
return {
shortCircuit: true,
url: String(resolvedUrl),
}
}
}
}

return nextResolve(specifier, context)
}

export const load: NodeJSLoader.Loader = async (url, context, nextLoad) => {
if (url.startsWith('file://')
&& /\.[tj]sx?(\?|$)/.test(url)
&& !url.includes('/node_modules/')
) {
try {
const moduleUrl = new URL(url)
const modulePath = fileURLToPath(moduleUrl)
const coverageVariable = moduleUrl.searchParams.get(PARAM_INSTRUMENT_COVERAGE_VAR)

const content = await fsPromise.readFile(modulePath)

const {code} = await transform(content.toString('utf8'), {
filename: modulePath,
jsc: {
target: 'es2022',
parser: /\.tsx?$/.test(modulePath)
? {
syntax: 'typescript',
tsx: /\.tsx$/.test(modulePath),
}
: {
syntax: 'ecmascript',
jsx: /\.jsx$/.test(modulePath),
},
preserveAllComments: true,
experimental: {
plugins: coverageVariable
? [
['swc-plugin-coverage-instrument', {
coverageVariable,
}],
]
: [],
},
},
sourceMaps: 'inline',
})

return {
shortCircuit: true,
format: 'module',
source: code,
}
} catch(e) {
console.error(e)
// let the next loader try to handle this
}
}

return nextLoad(url, context)
}
File renamed without changes.
Loading

0 comments on commit 7990398

Please sign in to comment.