Skip to content

Instantly share code, notes, and snippets.

@SMotaal
Last active October 4, 2017 16:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SMotaal/b2454f0c36d993f7bb4d503a16899c98 to your computer and use it in GitHub Desktop.
Save SMotaal/b2454f0c36d993f7bb4d503a16899c98 to your computer and use it in GitHub Desktop.
A simple TypeScript loader using NodeJS 9+ declarative loader API

A simple TypeScript loader using NodeJS 9+ declarative loader API

Purpose

This loader is intended to show very quickly how to use the proposed NodeJS 9.0.0 loader API to be able to compile TypeScript files on demand. It is not a stable solution in any way.

Using Node 9+ Loader

The proposed way of using the loader is as follows:

node --experimental-modules --loader ./loader.mjs imports.js

You will need to build a special version of NodeJS until this feature is released.

So you will likely need to use something like:

./node --experimental-modules --loader ./loader.mjs imports.js

Which means that by all means, this should not be used in any distributions at this point.

Building Node 9+

At this time, NodeJS 9.0 is still not released but can be manually built by checking out a specific branch from GitHub and working from there.

Checking out Guy Bedford's branch

Until a more stable branch is locked in we are resorting to the PR branch used by Guy to introduce the new declarative loader feature.

» git clone https://github.com/guybedford/node.git -b resolve-hook-rebased --single-branch

This will download roughly 275 MB's instead of over 3GB.

Building

NodeJS has a well documented BUILDING process in their repo on GitHub. However, you don't have to follow the entire process as outline, just pick the parts that will get you to a working node.

Keep in mind that you will likely repeat this process more than once until the point where it becomes a public release, so find a reproducable simple workflow.

Here is what I did on macOS High:

» git clone https://github.com/guybedford/node.git -b resolve-hook-rebased --single-branch node-gb

» cd node-gb

» ./configure

» make -j4

» ./node -e "console.log('Hello from Node.js ' + process.version)"

Here is a more up-to-date procedure:

» git clone https://github.com/guybedford/node.git -b resolve-hook-rebased --single-branch node-gb

» cd node-gb

» ./configuregit reset --hard 0bd9e6fa71b1baf54703e40c9b519b04f5b49197

» ./configure

» make -j4

» ./node -e "console.log('Hello from Node.js ' + process.version)"

or simply:

» git clone https://github.com/guybedford/node.git -b resolve-hook-rebased --single-branch node-gb; cd node-gb && git reset --hard 0bd9e6fa71b1baf54703e40c9b519b04f5b49197

» ./configure; make -j4; ./node -e "console.log('Hello from Node.js ' + process.version)";
import url from 'url';
import path from 'path';
import fs from 'fs';
import process from 'process';
import ts from "typescript";
const builtins = new Set(
Object.keys(process.binding('natives')).filter((str) =>
/^(?!(?:internal|node|v8)\/)/.test(str))
);
const JS_EXTENSIONS = new Set(['', '.js', '.mjs']);
const transpilers = {
'.ts': typeof ts !== 'undefined' && (() => {
const compilerOptions = {
module: ts.ModuleKind.ES2015,
inlineSourceMaps: true,
};
const compile = (pathname) => {
const input = fs.readFileSync(pathname).toString();
const { outputText, diagnostics, sourceMapText } = ts.transpileModule(input, { compilerOptions });
const dirname = path.dirname(pathname);
const basename = path.basename(pathname);
const outpath = path.join(dirname, `.${basename}.js`); // .replace(/(\.ts|)$/i, '.js')
fs.writeFileSync(outpath, outputText, { flag: 'w' });
return outpath;
}
const cache = new Map();
return (pathname) => {
// if (!fs.existsSync(pathname)) pathname = pathname + '.ts';
let compiledPath = cache.get(pathname);
if (!compiledPath)
cache.set(pathname, compiledPath = compile(pathname));
return compiledPath || null;
}
})(ts) || null,
}
export function resolve(specifier, parentModuleURL, defaultResolve) {
if (builtins.has(specifier)) {
return {
url: specifier,
format: 'builtin'
};
}
if (/^\.{0,2}[/]/.test(specifier) !== true && !specifier.startsWith('file:')) {
// For node_modules support:
return defaultResolve(specifier, parentModuleURL);
// throw new Error(`imports must begin with '/', './', or '../'; '${specifier}' does not`);
}
const resolved = new url.URL(specifier, parentModuleURL);
const ext = (path.extname(resolved.pathname) || (resolved.pathname += '.js', '.js')).toLowerCase();
if (!JS_EXTENSIONS.has(ext)) {
let pathname;
if (typeof transpilers[ext] === 'function')
pathname = transpilers[ext](resolved.pathname);
if (!pathname)
throw Error(`Cannot load file with non-JavaScript file extension ${ext}.`);
resolved.pathname = pathname;
}
return {
url: resolved.href,
format: 'esm'
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment