Skip to content

Commit

Permalink
docs: use
Browse files Browse the repository at this point in the history
Signed-off-by: Lexus Drumgold <unicornware@flexdevelopment.llc>
  • Loading branch information
unicornware committed Feb 7, 2023
1 parent e4efd39 commit 469f6d1
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,5 @@
"useGitignore": true,
"usePnP": false,
"version": "0.2",
"words": ["fldv", "flexdevelopmentllc", "mkbuild", "mlly"]
"words": ["fldv", "flexdevelopmentllc", "mkbuild", "mlly", "tscu"]
}
4 changes: 3 additions & 1 deletion .lintstagedrc.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"*": ["prettier --check", "cspell lint --color --no-progress --relative $@"],
"**/*.{cjs,gql,json,jsonc,md,mjs,ts,yml}": ["eslint --exit-on-fatal-error"],
"**/*.{cjs,gql,json,jsonc,md,mjs,mts,ts,yml}": [
"eslint --exit-on-fatal-error"
],
"**/*.{cjs,mjs,ts}": "vitest typecheck --changed --run",
"**/yarn.lock": "yarn dedupe --check",
"src/**/*.ts": "vitest --changed --coverage --run"
Expand Down
185 changes: 184 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,190 @@ yarn add @flex-development/tsconfig-utils@flex-development/tsconfig-utils

## Use

**TODO**: usage example.
Let's say a developer wants to run a TypeScript file using `node`. The developer has a [`tsconfig.json`](tsconfig.json)
with path alias configurations.

They implement [`loader.mjs`](loader.mjs) (and for good measure, [`typings/node/loader.d.ts`](typings/node/loader.d.ts)
too :wink:):

```typescript
/**
* @file Custom Loader Hooks
* @module loader
* @see https://nodejs.org/api/esm.html#loaders
*/

import * as mlly from '@flex-development/mlly'
import pathe from '@flex-development/pathe'
import * as tscu from '@flex-development/tsconfig-utils'
import * as esbuild from 'esbuild'
import { fileURLToPath, pathToFileURL } from 'node:url'

// add support for extensionless files in "bin" scripts
// https://github.com/nodejs/modules/issues/488
mlly.EXTENSION_FORMAT_MAP.set('', mlly.Format.COMMONJS)

/**
* URL of tsconfig file.
*
* @type {import('node:url').URL}
* @const tsconfig
*/
const tsconfig = mlly.toURL('tsconfig.json')

/**
* TypeScript compiler options.
*
* @type {tscu.CompilerOptions}
* @const compilerOptions
*/
const compilerOptions = tscu.loadCompilerOptions(tsconfig)

/**
* Determines how the module at the given `url` should be interpreted,
* retrieved, and parsed.
*
* @see {@linkcode LoadHookContext}
* @see https://nodejs.org/api/esm.html#loadurl-context-nextload
*
* @async
*
* @param {string} url - Resolved module URL
* @param {LoadHookContext} context - Hook context
* @return {Promise<LoadHookResult>} Hook result
*/
export const load = async (url, context) => {
// get module format
context.format = context.format ?? (await mlly.getFormat(url))

// validate import assertions
mlly.validateAssertions(url, context.format, context.importAssertions)

/**
* File extension of {@linkcode url}.
*
* @type {string}
* @const ext
*/
const ext = pathe.extname(url)

/**
* Source code.
*
* @type {Uint8Array | string | undefined}
* @var source
*/
let source = await mlly.getSource(url, { format: context.format })

// transform typescript files
if (/^\.(?:cts|mts|tsx?)$/.test(ext) && !/\.d\.(?:cts|mts|ts)$/.test(url)) {
// resolve path aliases
source = await tscu.resolvePaths(source, {
conditions: context.conditions,
ext: '',
parent: url,
tsconfig
})

// resolve modules
source = await mlly.resolveModules(source, {
conditions: context.conditions,
parent: url
})

// transpile source code
const { code } = await esbuild.transform(source, {
format: ext === '.cts' ? 'cjs' : 'esm',
loader: /^[cm]/.test(ext) ? 'ts' : ext.slice(1),
minify: false,
sourcefile: fileURLToPath(url),
sourcemap: 'inline',
tsconfigRaw: { compilerOptions }
})

// set source code to transpiled source
source = code
}

return { format: context.format, shortCircuit: true, source }
}

/**
* Resolves the given module `specifier`.
*
* Adds supports for:
*
* - Path alias resolution
* - Extensionless file and directory index resolution
*
* @see {@linkcode ResolveHookContext}
* @see https://nodejs.org/api/esm.html#resolvespecifier-context-nextresolve
*
* @async
*
* @param {string} specifier - Module specifier
* @param {ResolveHookContext} context - Hook context
* @return {Promise<ResolveHookResult>} Hook result
* @throws {Error}
*/
export const resolve = async (specifier, context) => {
const { conditions, parentURL: parent } = context

// resolve path alias
specifier = await mlly.resolveAlias(specifier, {
aliases: tscu.loadPaths(tsconfig),
conditions,
cwd: pathToFileURL(compilerOptions.baseUrl),
parent
})

/**
* Resolved module URL.
*
* @type {import('node:url').URL}
* @const url
*/
const url = await mlly.resolveModule(specifier, { conditions, parent })

return {
format: await mlly.getFormat(url),
shortCircuit: true,
url: url.href
}
}
```

The developer creates `scratch.ts` to test their custom loader hooks:

```typescript
/**
* @file Scratch
* @module scratch
*/

import { resolvePaths } from '@flex-development/tsconfig-utils'
import { dedent } from 'ts-dedent'

const code = dedent`
import type { ResolveAliasOptions } from '#src/interfaces'
import * as internal from '#src/internal'
import loadCompilerOptions from '#src/utils/load-compiler-options'
import * as mlly from '@flex-development/mlly'
`

console.debug(await resolvePaths(code, { ext: '', parent: import.meta.url }))
```

Running the file with `node --loader=./loader.mjs ./scratch` yields:

```sh
import type { ResolveAliasOptions } from './src/interfaces'
import * as internal from './src/internal'
import loadCompilerOptions from './src/utils/load-compiler-options'
import * as mlly from '@flex-development/mlly'
```
Pleased with their work, they crack open a cold Red Bull :blush:
## API
Expand Down
51 changes: 32 additions & 19 deletions loader.mjs
Original file line number Diff line number Diff line change
@@ -1,31 +1,41 @@
/**
* @file Custom Loader
* @file Custom Loader Hooks
* @module loader
* @see https://nodejs.org/docs/latest-v16.x/api/esm.html#loaders
* @see https://nodejs.org/api/esm.html#loaders
*/

import * as mlly from '@flex-development/mlly'
import pathe from '@flex-development/pathe'
import * as tscu from '@flex-development/tsconfig-utils'
import * as esbuild from 'esbuild'
import { fileURLToPath, pathToFileURL } from 'node:url'
import tsconfig from './tsconfig.json' assert { type: 'json' }

// add support for extensionless files in "bin" scripts
// https://github.com/nodejs/modules/issues/488
mlly.EXTENSION_FORMAT_MAP.set('', mlly.Format.COMMONJS)

/**
* URL of current working directory.
* URL of tsconfig file.
*
* @const {URL} cwd
* @type {import('node:url').URL}
* @const tsconfig
*/
const cwd = pathToFileURL(tsconfig.compilerOptions.baseUrl)
const tsconfig = mlly.toURL('tsconfig.json')

/**
* Determines how `url` should be interpreted, retrieved, and parsed.
* TypeScript compiler options.
*
* @type {tscu.CompilerOptions}
* @const compilerOptions
*/
const compilerOptions = tscu.loadCompilerOptions(tsconfig)

/**
* Determines how the module at the given `url` should be interpreted,
* retrieved, and parsed.
*
* @see {@linkcode LoadHookContext}
* @see https://nodejs.org/docs/latest-v16.x/api/esm.html#loadurl-context-nextload
* @see https://nodejs.org/api/esm.html#loadurl-context-nextload
*
* @async
*
Expand All @@ -43,25 +53,27 @@ export const load = async (url, context) => {
/**
* File extension of {@linkcode url}.
*
* @const {string} ext
* @type {string}
* @const ext
*/
const ext = pathe.extname(url)

/**
* Source code.
*
* @var {Uint8Array | string | undefined} source
* @type {Uint8Array | string | undefined}
* @var source
*/
let source = await mlly.getSource(url, { format: context.format })

// transform typescript files
if (/^\.(?:cts|mts|tsx?)$/.test(ext) && !/\.d\.(?:cts|mts|ts)$/.test(url)) {
// resolve path aliases
source = await mlly.resolveAliases(source, {
aliases: tsconfig.compilerOptions.paths,
source = await tscu.resolvePaths(source, {
conditions: context.conditions,
cwd,
parent: url
ext: '',
parent: url,
tsconfig
})

// resolve modules
Expand All @@ -77,7 +89,7 @@ export const load = async (url, context) => {
minify: false,
sourcefile: fileURLToPath(url),
sourcemap: 'inline',
tsconfigRaw: { compilerOptions: tsconfig.compilerOptions }
tsconfigRaw: { compilerOptions }
})

// set source code to transpiled source
Expand All @@ -96,7 +108,7 @@ export const load = async (url, context) => {
* - Extensionless file and directory index resolution
*
* @see {@linkcode ResolveHookContext}
* @see https://nodejs.org/docs/latest-v16.x/api/esm.html#resolvespecifier-context-nextresolve
* @see https://nodejs.org/api/esm.html#resolvespecifier-context-nextresolve
*
* @async
*
Expand All @@ -110,16 +122,17 @@ export const resolve = async (specifier, context) => {

// resolve path alias
specifier = await mlly.resolveAlias(specifier, {
aliases: tsconfig.compilerOptions.paths,
aliases: tscu.loadPaths(tsconfig),
conditions,
cwd,
cwd: pathToFileURL(compilerOptions.baseUrl),
parent
})

/**
* Resolved module URL.
*
* @const {URL} url
* @type {import('node:url').URL}
* @const url
*/
const url = await mlly.resolveModule(specifier, { conditions, parent })

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@
"types": "./dist/index.d.mts",
"scripts": {
"build": "mkbuild",
"changelog": "node --loader=./loader.mjs ./changelog.config.ts",
"changelog": "node --loader=./loader.mjs ./changelog.config",
"check:ci": "yarn dedupe --check && yarn check:format && yarn check:lint && yarn check:spelling && yarn typecheck && yarn test:cov && NODE_ENV=production yarn pack -o %s-%v.tgz && yarn clean:pack && yarn check:types:build",
"check:format": "prettier --check .",
"check:lint": "eslint --exit-on-fatal-error --ext cjs,gql,json,jsonc,md,mjs,ts,yml --max-warnings 0 .",
"check:lint": "eslint --exit-on-fatal-error --ext cjs,gql,json,jsonc,md,mjs,mts,ts,yml --max-warnings 0 .",
"check:spelling": "cspell lint --color --no-progress --relative $@ \"**\"",
"check:types": "tsc -p tsconfig.typecheck.json",
"check:types:build": "bash ./scripts/typecheck-build.sh",
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,5 @@
"useUnknownInCatchVariables": true
},
"exclude": ["**/coverage", "**/dist", "**/node_modules"],
"include": ["**/**.cjs", "**/**.mjs", "**/**.ts", "**/.*.cjs"]
"include": ["**/**.cjs", "**/**.mjs", "**/**.mts", "**/**.ts", "**/.*.cjs"]
}
3 changes: 3 additions & 0 deletions typings/@flex-development/tsconfig-utils/index.d.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare module '@flex-development/tsconfig-utils' {
export * from '#src'
}

0 comments on commit 469f6d1

Please sign in to comment.