Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CoffeeScript ESM loader #5423

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Cakefile
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,13 @@ buildParser = ->
buildExceptParser = (callback) ->
files = fs.readdirSync 'src'
files = ('src/' + file for file in files when file.match(/\.(lit)?coffee$/))
run ['-c', '-o', 'lib/coffeescript'].concat(files), callback
run ['-c', '-o', 'lib/coffeescript'].concat(files), ->
dir = 'lib/coffeescript/'
loader = dir+'loader.'
fs.renameSync(loader+'js',loader+'mjs')
index = dir+'index.'
fs.copyFileSync(index+'js',index+'cjs')
callback?.apply?(@,arguments)

build = (callback) ->
buildParser()
Expand Down
217 changes: 217 additions & 0 deletions lib/coffeescript/index.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
// Generated by CoffeeScript 2.7.0
(function() {
// Node.js Implementation
var CoffeeScript, ext, fs, helpers, i, len, path, ref, universalCompile, vm,
hasProp = {}.hasOwnProperty;

CoffeeScript = require('./coffeescript');

fs = require('fs');

vm = require('vm');

path = require('path');

helpers = CoffeeScript.helpers;

CoffeeScript.transpile = function(js, options) {
var babel;
try {
babel = require('@babel/core');
} catch (error) {
try {
babel = require('babel-core');
} catch (error) {
// This error is only for Node, as CLI users will see a different error
// earlier if they don’t have Babel installed.
throw new Error('To use the transpile option, you must have the \'@babel/core\' module installed');
}
}
return babel.transform(js, options);
};

// The `compile` method shared by the CLI, Node and browser APIs.
universalCompile = CoffeeScript.compile;

// The `compile` method particular to the Node API.
CoffeeScript.compile = function(code, options) {
// Pass a reference to Babel into the compiler, so that the transpile option
// is available in the Node API. We need to do this so that tools like Webpack
// can `require('coffeescript')` and build correctly, without trying to
// require Babel.
if (options != null ? options.transpile : void 0) {
options.transpile.transpile = CoffeeScript.transpile;
}
return universalCompile.call(CoffeeScript, code, options);
};

// Compile and execute a string of CoffeeScript (on the server), correctly
// setting `__filename`, `__dirname`, and relative `require()`.
CoffeeScript.run = function(code, options = {}) {
var answer, dir, mainModule, ref;
mainModule = require.main;
// Set the filename.
mainModule.filename = process.argv[1] = options.filename ? fs.realpathSync(options.filename) : helpers.anonymousFileName();
// Clear the module cache.
mainModule.moduleCache && (mainModule.moduleCache = {});
// Assign paths for node_modules loading
dir = options.filename != null ? path.dirname(fs.realpathSync(options.filename)) : fs.realpathSync('.');
mainModule.paths = require('module')._nodeModulePaths(dir);
// Save the options for compiling child imports.
mainModule.options = options;
options.filename = mainModule.filename;
options.inlineMap = true;
// Compile.
answer = CoffeeScript.compile(code, options);
code = (ref = answer.js) != null ? ref : answer;
return mainModule._compile(code, mainModule.filename);
};

// Compile and evaluate a string of CoffeeScript (in a Node.js-like environment).
// The CoffeeScript REPL uses this to run the input.
CoffeeScript.eval = function(code, options = {}) {
var Module, _module, _require, createContext, i, isContext, js, k, len, o, r, ref, ref1, ref2, ref3, sandbox, v;
if (!(code = code.trim())) {
return;
}
createContext = (ref = vm.Script.createContext) != null ? ref : vm.createContext;
isContext = (ref1 = vm.isContext) != null ? ref1 : function(ctx) {
return options.sandbox instanceof createContext().constructor;
};
if (createContext) {
if (options.sandbox != null) {
if (isContext(options.sandbox)) {
sandbox = options.sandbox;
} else {
sandbox = createContext();
ref2 = options.sandbox;
for (k in ref2) {
if (!hasProp.call(ref2, k)) continue;
v = ref2[k];
sandbox[k] = v;
}
}
sandbox.global = sandbox.root = sandbox.GLOBAL = sandbox;
} else {
sandbox = global;
}
sandbox.__filename = options.filename || 'eval';
sandbox.__dirname = path.dirname(sandbox.__filename);
// define module/require only if they chose not to specify their own
if (!(sandbox !== global || sandbox.module || sandbox.require)) {
Module = require('module');
sandbox.module = _module = new Module(options.modulename || 'eval');
sandbox.require = _require = function(path) {
return Module._load(path, _module, true);
};
_module.filename = sandbox.__filename;
ref3 = Object.getOwnPropertyNames(require);
for (i = 0, len = ref3.length; i < len; i++) {
r = ref3[i];
if (r !== 'paths' && r !== 'arguments' && r !== 'caller') {
_require[r] = require[r];
}
}
// use the same hack node currently uses for their own REPL
_require.paths = _module.paths = Module._nodeModulePaths(process.cwd());
_require.resolve = function(request) {
return Module._resolveFilename(request, _module);
};
}
}
o = {};
for (k in options) {
if (!hasProp.call(options, k)) continue;
v = options[k];
o[k] = v;
}
o.bare = true; // ensure return value
js = CoffeeScript.compile(code, o);
if (sandbox === global) {
return vm.runInThisContext(js);
} else {
return vm.runInContext(js, sandbox);
}
};

CoffeeScript.register = function() {
return require('./register');
};

// Throw error with deprecation warning when depending upon implicit `require.extensions` registration
if (require.extensions) {
ref = CoffeeScript.FILE_EXTENSIONS;
for (i = 0, len = ref.length; i < len; i++) {
ext = ref[i];
(function(ext) {
var base;
return (base = require.extensions)[ext] != null ? base[ext] : base[ext] = function() {
throw new Error(`Use CoffeeScript.register() or require the coffeescript/register module to require ${ext} files.`);
};
})(ext);
}
}

CoffeeScript._compileRawFileContent = function(raw, filename, options = {}) {
var answer, err, stripped;
// Strip the Unicode byte order mark, if this file begins with one.
stripped = raw.charCodeAt(0) === 0xFEFF ? raw.substring(1) : raw;
options = Object.assign({}, options, {
filename: filename,
literate: helpers.isLiterate(filename),
sourceFiles: [filename]
});
try {
answer = CoffeeScript.compile(stripped, options);
} catch (error) {
err = error;
// As the filename and code of a dynamically loaded file will be different
// from the original file compiled with CoffeeScript.run, add that
// information to error so it can be pretty-printed later.
throw helpers.updateSyntaxError(err, stripped, filename);
}
return answer;
};

CoffeeScript._compileFile = function(filename, options = {}) {
var raw;
raw = fs.readFileSync(filename, 'utf8');
return CoffeeScript._compileRawFileContent(raw, filename, options);
};

module.exports = CoffeeScript;

// Explicitly define all named exports so that Node’s automatic detection of
// named exports from CommonJS packages finds all of them. This enables consuming
// packages to write code like `import { compile } from 'coffeescript'`.
// Don’t simplify this into a loop or similar; the `module.exports.name` part is
// essential for Node’s algorithm to successfully detect the name.
module.exports.VERSION = CoffeeScript.VERSION;

module.exports.FILE_EXTENSIONS = CoffeeScript.FILE_EXTENSIONS;

module.exports.helpers = CoffeeScript.helpers;

module.exports.registerCompiled = CoffeeScript.registerCompiled;

module.exports.compile = CoffeeScript.compile;

module.exports.tokens = CoffeeScript.tokens;

module.exports.nodes = CoffeeScript.nodes;

module.exports.register = CoffeeScript.register;

module.exports.eval = CoffeeScript.eval;

module.exports.run = CoffeeScript.run;

module.exports.transpile = CoffeeScript.transpile;

module.exports.patchStackTrace = CoffeeScript.patchStackTrace;

module.exports._compileRawFileContent = CoffeeScript._compileRawFileContent;

module.exports._compileFile = CoffeeScript._compileFile;

}).call(this);
100 changes: 100 additions & 0 deletions lib/coffeescript/loader.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Generated by CoffeeScript 2.7.0
//!/usr/bin/env coffee
var baseURL, getPackageType, is_coffee;

import {
readFile
} from "fs/promises";

import {
readFileSync
} from "fs";

import {
createRequire
} from "module";

import {
dirname,
extname,
resolve as resolvePath
} from "path";

import {
cwd
} from "process";

import {
fileURLToPath,
pathToFileURL
} from "url";

import CoffeeScript from "./index.cjs";

baseURL = pathToFileURL(`${cwd}/`).href;

is_coffee = (specifier) => {
return specifier.slice(specifier.lastIndexOf(".") + 1) === 'coffee';
};

export var resolve = (specifier, context, defaultResolve) => {
var parentURL;
({
parentURL = baseURL
} = context);
if (is_coffee(specifier)) {
return {
shortCircuit: true,
url: new URL(specifier, parentURL).href
};
}
return defaultResolve(specifier, context, defaultResolve);
};

export var load = async (url, context, defaultLoad) => {
var format, rawSource, transformedSource;
if (is_coffee(url)) {
format = (await getPackageType(url));
if (format === "commonjs") {
return {
format
};
}
({
source: rawSource
} = (await defaultLoad(url, {
format
})));
transformedSource = CoffeeScript.compile(rawSource.toString(), {
bare: true,
filename: url,
inlineMap: true
});
return {
format,
source: transformedSource
};
//console.log('!!!', )
}
return defaultLoad(url, context, defaultLoad);
};

getPackageType = async (url) => {
var dir, isFilePath, packagePath, type;
isFilePath = !!extname(url);
dir = isFilePath ? dirname(fileURLToPath(url)) : url;
packagePath = resolvePath(dir, "package.json");
type = (await readFile(packagePath, {
encoding: "utf8"
}).then((filestring) => {
return JSON.parse(filestring).type;
}).catch((err) => {
if ((err != null ? err.code : void 0) !== "ENOENT") {
return console.error(err);
}
}));
if (type) {
return type;
}
return dir.length > 1 && getPackageType(resolvePath(dir, ".."));
};
1 change: 1 addition & 0 deletions loader.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './lib/coffeescript/loader.mjs';
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"bin",
"lib",
"register.js",
"loader.mjs",
"repl.js"
],
"scripts": {
Expand Down
Loading