mattdesl / mattdesl/esmify

easy life

Open
#4 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
119
Forks
11
PR merge metrics
No merged PRs in 30d

Description

enableLegacyBabel5ModuleInterop: true is good with browserify
there's a prod mode setting for sucrase also, i suppose its a free envify
at least 10x less memory use than webpack, sadly that a true statement lol

```js
var stream = require("stream");
var util = require("util");
var path = require("path");

let sucrase = require("sucrase")

module.exports = buildTransform();
module.exports.configure = buildTransform;

/** @type import('sucrase').Options */
var sucraseConfig = file => ({
transforms: ["typescript", "imports", "jsx"],
filePath: file,
//enableLegacyTypeScriptModuleInterop: true
enableLegacyBabel5ModuleInterop: true
})

/** @type import('sucrase').Options */
var sucraseHotConfig = file => ({
transforms: ["typescript", "imports", "jsx", "react-hot-loader"],
filePath: file,
//enableLegacyTypeScriptModuleInterop: true
enableLegacyBabel5ModuleInterop: true
})

function buildTransform() {
return function (filename) {
const babelOpts = sucraseConfig(filename)
if (babelOpts === null) {
return stream.PassThrough();
}
return new SucraseStream(babelOpts);
};
}

class SucraseStream extends stream.Transform {
constructor(opts) {
super();
this._data = [];
this._opts = opts;
}

_transform(buf, enc, callback) {
this._data.push(buf);
callback();
}

_flush(callback) {
// Merge the buffer pieces after all are available
const data = Buffer.concat(this._data).toString();

try{
let result = sucrase.transform(data, this._opts)
var code = result !== null ? result.code : data;
this.push(code);
callback();
}
catch(e){
callback(e)
}
}
}
```

you can use this to auto alias everything in src (or wherever, easy to change) so you can use absolute paths np. I suppose you could do some charCode string checks for 'react' to replace with 'preact' or something like that, but meh.
```
var path = require('path')
const { Transform, PassThrough } = require('stream')

/** type{(file: ReadonlyArray, dir: string)}*/
function parseImports(file, dir) {
const results = file.map((line, index) => {
const imports = findImport(line);

if (imports === null) {
return null;
}

return {
path: dir,
index,
import: imports,
};
});

return results.filter((value) => {
return value !== null && value !== undefined;
});
}

/**
* @type{(line: string)}
* @returns {string | null}
*/
function findImport(line) {
const matches = line.match(/from (["'])(.*?)\1/) || line.match(/import\((["'])(.*?)\1\)/) || line.match(/require\((["'])(.*?)\1\)/);

if (!matches) {
return null;
}

const multiple = [/from (["'])(.*?)\1/g, /import\((["'])(.*?)\1\)/g, /require\((["'])(.*?)\1\)/g].some((exp) => {
const results = line.match(exp);

//console.log(results)
//
//return results && results.length > 1

if (results && results.length > 1) {
results.map(r => findImport(r))
}
})

// if (multiple) {
//throw new Error('Multiple imports on the same line are currently not supported!');
//jk fixed with recursion
// }

return matches[2];
}

/** @type {(file: ReadonlyArray, imports: FileData[], options: CompilerOptions) => string[]) }
* @returns : string[]
*/
function resolveImports(file, imports, options) {
const { baseUrl, paths } = options;

/** @type { [key: string]: string[] | undefined } */
const aliases = {};
for (const alias in paths) {
/* istanbul ignore else */
if (paths.hasOwnProperty(alias)) {
let resolved = alias;
if (alias.endsWith('/*')) {
resolved = alias.replace('/*', '/');
}

aliases[resolved] = paths[alias];
}
}

const lines = [...file];
for (const imported of imports) {
const line = file[imported.index];

let resolved = '';
for (const alias in aliases) {
/* istanbul ignore else */
if (aliases.hasOwnProperty(alias) && imported.import.startsWith(alias)) {
const choices = aliases[alias];

if (choices != undefined) {
resolved = choices[0];
if (resolved.endsWith('/*')) {
resolved = resolved.replace('/*', '/');
}

resolved = imported.import.replace(alias, resolved);

break;
}
}
}

if (resolved.length < 1) {
continue;
}

let relative = path.relative(path.dirname(imported.path), baseUrl || './');
relative = path.join(relative, resolved);
relative = path.relative(path.dirname(imported.path), path.resolve(path.dirname(imported.path), relative));
relative = relative.replace(/\\/g, '/');

if (relative.length === 0 || !relative.startsWith('.')) {
relative = './' + relative;
}

lines[imported.index] = line.replace(imported.import, relative);
}

return lines;
}

const fs = require('fs')
const sourceDirs = fs.readdirSync('src').filter(f => (f.indexOf(".") < 1))

let createConfig = () => {
//todo , merge this with fs read tsconfig + user options + maybe like a web_modules
const config = {
baseUrl: ".",
paths: {}
}
for (var x of sourceDirs) {
config.paths[`${x}/*`] = [`src/${x}/*`]
}
//return JSON.stringify(config)
return config
}

const compilerOptions = createConfig()

function compile(filePath, chunk) {
//const lines = chunk.toString('utf8').split('\n');
const lines = chunk.split('\n');
const imports = parseImports(lines, filePath)
const code = resolveImports(lines, imports, compilerOptions).join('\n')
return code
}

const unpathifyStream = (file) => {
if (!/\.tsx?$|\.jsx?$/.test(file) || file.indexOf("node_modules") > 0 || file.indexOf("src") < 0) {
return new PassThrough();
}

var _transform = new Transform()
_transform._write = (chunk, encoding, next) => {
_transform.push(compile(file, chunk.toString('utf8')))
next();
}
return _transform
}

module.exports = unpathifyStream
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by reviewing the repository entry point and the existing transform pipeline; the issue only identifies src as the directory to process and provides proposed Sucrase and import-aliasing code. Clarify whether both transformations are in scope, then define behavior and tests with maintainers before implementing anything.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
build-system, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.