Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

atbuild

Package Overview
Dependencies
Maintainers
1
Versions
42
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

atbuild - npm Package Compare versions

Comparing version 1.3.3 to 1.5.0

dist/atbuild.test.js

427

dist/atbuild.js

@@ -1,24 +0,44 @@

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
var __export = (target, all) => {
__markAsModule(target);
for (var name in all)
__defProp(target, name, {get: all[name], enumerable: true});
};
var __exportStar = (target, module2, desc) => {
__markAsModule(target);
if (typeof module2 === "object" || typeof module2 === "function") {
for (let key of __getOwnPropNames(module2))
if (!__hasOwnProp.call(target, key) && key !== "default")
__defProp(target, key, {get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable});
}
return target;
};
var __toModule = (module2) => {
if (module2 && module2.__esModule)
return module2;
return __exportStar(__defProp(__create(__getProtoOf(module2)), "default", {value: module2, enumerable: true}), module2);
};
__export(exports, {
AtBuild: () => AtBuild,
buildAST: () => fullAst.buildAST,
default: () => $,
requireFromString: () => requireFromString,
transformAST: () => fullAst.transformAST
});
exports.default = exports.AtBuild = void 0;
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
const fullAst = __toModule(require("./fullAst"));
let fs;
const BUILD_TIME_MATCHER = /^\s*@(.*)/;
const MULTILINE_BUILD_TIME_MATCHER = /^\s*@@(.*)/;
const RUNTIME_MATCHER = /\@\{([^@}]*)\}/gm;
const HEADER_STRING = "/* eslint-disable */" + "\n" + "// @ts-nocheck" + "\n" + "// @ts-ignore\n" + "// @noflow\n" + '"use strict";\n\n';
const getMaxLine = function (currentLine, node) {
const HEADER_STRING = '/* eslint-disable */\n// @ts-nocheck\n// @ts-ignore\n// @noflow\n"use strict";\n\n';
const getMaxLine = function(currentLine, node) {
return Math.max(currentLine, node.lineNumber);
};
let requireFromString;
if (process.env.WEB) {
requireFromString = code => eval(`
let bundle;
if (false) {
requireFromString = (code) => eval(`
() => {

@@ -30,236 +50,12 @@ var exports = {default: null};

} else {
requireFromString = require("./requireFromString");
requireFromString = require("./requireFromString").requireFromString;
fs = require("fs");
}
class AtBuild {
static buildAST(code) {
const nodes = [];
let lineMatch = null;
let lines = String(code).split("\n");
let isMultiline = false;
for (let i = 0; i < lines.length; i++) {
if (lines[i].trim().length === 0) {
continue;
}
if (lineMatch = lines[i].match(MULTILINE_BUILD_TIME_MATCHER)) {
if (isMultiline) {
isMultiline = false;
} else {
isMultiline = true;
}
let scopeValue = 0;
if (lineMatch[1].trimEnd().endsWith("{")) {
scopeValue++;
} else if (lineMatch[1].trimEnd().endsWith("{")) {
scopeValue--;
}
let string = lineMatch[1];
for (let i = 0; i < lineMatch[0].indexOf(lineMatch[1]) + 1; i++) {
string = " " + string;
}
nodes.push({
lineNumber: i,
type: "MultilineBuildtimeCode",
value: string,
scope: scopeValue
});
} else if (isMultiline) {
nodes.push({
lineNumber: i,
type: "MultilineBuildtimeCode",
value: lines[i] // scope: scopeValue,
});
} else if (lineMatch = lines[i].match(BUILD_TIME_MATCHER)) {
let scopeValue = 0;
if (lineMatch[1].trimEnd().endsWith("{")) {
scopeValue++;
} else if (lineMatch[1].trimEnd().endsWith("{")) {
scopeValue--;
}
let string = lineMatch[1];
for (let i = 0; i < lineMatch[0].indexOf(lineMatch[1]) + 1; i++) {
string = " " + string;
}
nodes.push({
lineNumber: i,
type: "BuildtimeCode",
value: string,
scope: scopeValue
});
} else {
let line = [{
lineNumber: i,
type: "RuntimecodeLineStart"
}, {
lineNumber: i,
type: "RuntimeCode",
value: lines[i],
column: 0
}];
let result;
let lineToMatch = lines[i];
let offset = 0;
for (let result of lineToMatch.matchAll(RUNTIME_MATCHER)) {
const [input, match] = result;
const index = result.index;
const original = line[line.length - 1].value;
line[line.length - 1].value = original.substring(0, index - offset);
line.length += 2;
line[line.length - 2] = {
lineNumber: i,
type: "InterpolatedCode",
value: match,
column: index - offset
};
lineToMatch = result.input.substring(offset = index + input.length);
line[line.length - 1] = {
type: "RuntimeCode",
lineNumber: i,
value: lineToMatch,
column: offset
};
}
nodes.push(...line);
nodes.push({
lineNumber: i,
type: "RuntimecodeLineEnd"
});
}
}
return nodes;
return fullAst.buildAST(code);
}
static transformASTBuildTimeOnly(nodes) {
const maxLineNumber = nodes.reduce(getMaxLine, 0);
let lines = new Array(maxLineNumber + 1);
for (let i = 0; i < lines.length; i++) {
lines[i] = "";
}
for (let node of nodes) {
switch (node.type) {
case "BuildtimeCode":
{
lines[node.lineNumber] += node.value;
if (!lines[node.lineNumber].endsWith("\n")) {
lines[node.lineNumber] += "\n";
}
break;
}
case "InterpolatedCode":
{
lines[node.lineNumber] += "${" + node.value + "}";
break;
}
case "RuntimeCode":
{
lines[node.lineNumber] += node.value;
break;
}
case "RuntimecodeLineStart":
{
lines[node.lineNumber] += "`";
break;
}
case "RuntimecodeLineEnd":
{
lines[node.lineNumber] += "`;\n";
break;
}
}
}
return lines.join("");
}
static transformAST(nodes, asFunction) {
let code;
if (asFunction) {
code = "module.exports.default = async function __atBuild(require) { var __CODE__ = [];\n\n";
} else {
code = "var __CODE__ = [];\n\n";
}
const maxLineNumber = nodes.reduce(getMaxLine, 0);
let lines = new Array(maxLineNumber + 3);
for (let i = 0; i < lines.length; i++) {
lines[i] = "";
}
for (let node of nodes) {
switch (node.type) {
case "MultilineBuildtimeCode":
case "BuildtimeCode":
{
lines[node.lineNumber] += node.value + "\n";
break;
}
case "InterpolatedCode":
{
lines[node.lineNumber] += "${" + node.value + "}";
break;
}
case "RuntimeCode":
{
// prettier-ignore
lines[node.lineNumber] += node.value.replace(/`/igm, "\\`");
break;
}
case "RuntimecodeLineStart":
{
lines[node.lineNumber] += "__CODE__.push(`";
break;
}
case "RuntimecodeLineEnd":
{
lines[node.lineNumber] += "`);\n";
break;
}
}
}
lines.unshift(code);
if (asFunction) {
lines[lines.length - 1] = `return __CODE__.join("\\n");\n}; module.exports.__specialInitFunction = true;`;
} else {
lines[lines.length - 1] = `module.exports.default = __CODE__.join("\\n");`;
}
return lines.join("");
}
static *findNodesAtLine(nodes, lineNumber) {
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (node.lineNumber === lineNumber) {

@@ -270,140 +66,19 @@ yield node;

}
static transformASTForLineColumn(nodes, lineNumber, column, response) {
// go to the line.
let lineNode = this.findNodesAtLine(nodes, lineNumber).next().value;
if (!lineNode) {
response[0] = "";
response[1] = this.ASTResponseType.RuntimeCode;
response[2] = lineNumber;
response[3] = column;
return;
}
if (lineNode.type === "BuildtimeCode" || lineNode.type === "InterpolatedCode") {
response[0] = this.transformASTBuildTimeOnly(nodes);
response[1] = this.ASTResponseType.BuildtimeCode;
response[2] = lineNumber;
response[3] = column;
return;
}
let code = "var __CODE__ = [];\n\n";
const maxLineNumber = nodes.reduce(getMaxLine, 0);
let lines = new Array(maxLineNumber + 3);
for (let i = 0; i < lines.length; i++) {
lines[i] = "";
}
let lineOffset = 0;
for (let node of nodes) {
switch (node.type) {
case "BuildtimeCode":
{
lines[node.lineNumber] += node.value + "\n";
lineOffset++;
break;
}
case "InterpolatedCode":
{
lines[node.lineNumber] += "${" + node.value + "}";
break;
}
case "RuntimeCode":
{
lines[node.lineNumber] += `/* ATBuildColumnMap: ${node.column} */` + node.value;
break;
}
case "RuntimecodeLineStart":
{
lines[node.lineNumber] += `__CODE__.push(\`/* ATBuildLineMap: ${node.lineNumber} */`;
break;
}
case "RuntimecodeLineEnd":
{
lines[node.lineNumber] += "`);\n";
break;
}
}
}
lines.unshift(code);
lines[lines.length - 1] = `module.exports = __CODE__.join("\\n");`;
response[0] = lines.join("\n");
response[1] = this.ASTResponseType.RuntimeCode;
response[2] = lineNumber;
response[3] = column;
return;
}
static extractSourceAndType(code, filepath, line, column, response) {
const ast = AtBuild.buildAST(code);
response[2] = response[3] = 0;
AtBuild.transformASTForLineColumn(ast, line, column, response);
if (response[0] !== "" && response[1] === this.ASTResponseType.RuntimeCode) {
const source = this._eval(response[0], filepath, false);
const lines = source.split("\n");
for (let i = 0; i < lines.length; i++) {
if (lines[i].indexOf(`/* AtBuildLineMap: ${line} */`) > -1) {
response[2] = i;
for (let offset = 0; offset < lines[i]; offset++) {
const line = lines[i].substring(offset);
let _offset = line.indexOf(`/* AtBuildColumnMap: ${column} */`);
let match = null;
if (_offset > -1) {
response[3] = offset + _offset;
break;
} else if (match = line.match(/\/\* AtBuildColumnMap: (\d*) \*\//)) {}
}
response[3] = lines[i].indexOf(`/* AtBuildColumnMap: ${column} */`);
break;
}
}
response[0] = source.replace(/\/\* AtBuildColumnMap: \d* \*\//gim, "").replace(/\/\* AtBuildLineMap: \d* \*\//gim, "");
}
}
static evalFile(path, header) {
return this.eval(fs.readFileSync(path), path, header, module.parent);
}
static async evalFileAsync(path, header) {
return await this.evalAsync(fs.readFileSync(path), path, header, module.parent);
}
static _eval(code, filepath = null, addHeader = false, requireFunc = module.require) {
let source = requireFromString(code, filepath, requireFunc);
if (addHeader) {
source = HEADER_STRING + source;
source += `
module.exports = __atBuild
`;
}
return source;
}
static eval(code, filepath = null, addHeader = false, requireFunc = module.require) {
const ast = AtBuild.buildAST(code);
const processed = AtBuild.transformAST(ast, false);
const processed = AtBuild.transformAST(ast, code);
const res = this._eval(processed, filepath, addHeader, requireFunc);
if (res && res.default) {

@@ -415,25 +90,19 @@ return res.default;

}
static async evalAsync(code, filepath = null, addHeader = false, requireFunc = module.require) {
const ast = AtBuild.buildAST(code);
const processed = AtBuild.transformAST(ast, true);
const processed = AtBuild.transformAST(ast, code);
let source = await requireFromString(processed, filepath, requireFunc);
if (addHeader) {
source = HEADER_STRING + source;
}
return source;
}
}
exports.AtBuild = AtBuild;
_defineProperty(AtBuild, "ASTResponseType", {
AtBuild.transformAST = fullAst.transformAST;
AtBuild.ASTResponseType = {
BuildtimeCode: 0,
RuntimeCode: 1
});
var _default = AtBuild;
exports.default = _default;
};
function $(arg) {
return arg;
}
#!/usr/bin/env node
"use strict";
const {performance} = require("perf_hooks");
const meow = require("meow");
const {
AtBuild
} = require("atbuild");
const {AtBuild} = require("atbuild");
const fs = require("fs");
const path = require("path");
const {buildAST, transformAST} = require("./light");
let bundle;
const cli = meow(`
AtBuild is a JavaScript preprocessor language. It lets you use JavaScript to write JavaScript, so you can easily move slow code from runtime to build-time.
There are only two rules.

@@ -25,14 +22,29 @@

Usage
$ atbuild input.@js [destination]
$ atbuild input.js [destination]
Options
--header [true]: Include a header to let Flow, TypeScript, and ESLint know to ignore the file.
--no-header [false]: Skip the header
--pretty [false]: Run Prettier on the output. Requires "prettier" to be installed globally.
--eval [false]: After transforming, eval the output.
--bundle [true]: Runs esbuild to resolve the modules for the backend code and then evals. This is what the webpack-loader does.
--ast: Print the ast & exit
--print: Print the generated code & exit
--mode [auto] Determines flavor of Atbuild to use.
Can be: "light", "full", "auto"
- "auto": decide automatically based on the extension, default to "light"
- "light": regular JavaScript/TypeScript files
- "full": .@js or .jsb files (Atbuild)
--types [true] Autogenerates a .ts.d file along with your code. This writes to disk. Requires bundle.
--no-types Improves performance by 2x-5x.
--format [esm] "cjs" | "esm" | "iife". This option is passed to esbuild (https://esbuild.github.io/api/#format).
--outdir When bundling multiple files, output them in this directory instead
Examples
$ atbuild ./file.@js ./file.js
$ atbuild ./file.js ./file.out.js
$ atbuild ./file.@js
$ atbuild ./file.@js | node # Runs the file
`, {
flags: {
verbose: {
type: "boolean",
default: !!process.env.FORCE_DEV,
isMultiple: false,
isRequired: false
},
header: {

@@ -44,5 +56,43 @@ type: "boolean",

},
pretty: {
outdir: {
type: "string",
default: "",
isMultiple: false,
isRequired: false
},
types: {
type: "boolean",
default: false
default: true,
isMultiple: false,
isRequired: false
},
format: {
type: "string",
default: "esm",
isMultiple: false,
isRequired: false
},
ast: {
type: "boolean",
default: false,
isMultiple: false,
isRequired: false
},
print: {
type: "boolean",
default: false,
isMultiple: false,
isRequired: false
},
bundle: {
type: "boolean",
default: true,
isMultiple: false,
isRequired: false
},
mode: {
type: "string",
default: "auto",
isMultiple: false,
isRequired: true
}

@@ -52,3 +102,2 @@ }

let input;
try {

@@ -64,28 +113,84 @@ input = cli.input[0];

}
async function run() {
const output = await AtBuild.evalFileAsync(input, cli.flags.header);
if (cli.flags.pretty) {
try {
const prettier = require("prettier");
output = prettier.format(output, {
parser: "babel-flow",
filepath: input
});
} catch (exception) {
console.error("--pretty failed: Prettier isn't installed?");
process.exit(1);
if (!input) {
cli.showHelp(1);
}
async function run(input2, destination, directory) {
const source = await fs.promises.readFile(input2, "utf8");
let output;
let _mode = {auto: "auto", light: "light", full: "full"}[cli.flags.mode] || "auto";
let start;
if (cli.flags.verbose) {
start = performance.now();
}
if (cli.flags.bundle) {
output = await bundle(source, {
filename: input2,
destination,
directory: directory || destination,
mode: cli.flags.mode,
typescript: cli.flags.types,
filepath: input2,
format: cli.flags.format,
defaultMode: _mode
});
}
if (!cli.flags.bundle) {
if (_mode === "auto") {
const extension = path.extname(input2);
_mode = {
".js": "light",
".jsx": "light",
".ts": "light",
".tsx": "light",
".@js": "full",
".jsb": "full",
".@ts": "full",
".tsb": "full"
}[extension] || "light";
}
if (cli.flags.ast && _mode === "light") {
console.log([...buildAST(source)]);
} else if (cli.flags.ast) {
console.log([...AtBuild.buildAST(source)]);
} else {
cli.flags.print = true;
}
if (cli.flags.print && _mode === "light") {
console.log(transformAST(buildAST(source)));
process.exit(0);
} else if (cli.flags.print) {
console.log(AtBuild.transformAST(AtBuild.buildAST(source)));
process.exit(0);
}
}
if (cli.input[1]) {
fs.writeFileSync(cli.input[1], output);
process.exit(0);
if (destination) {
await fs.promises.writeFile(destination, output || source, "utf8");
console.log("Wrote", destination);
} else {
console.log(output);
}
if (cli.flags.verbose) {
console.log("Finished", input2, "in", (performance.now() - start).toFixed(4));
}
}
run();
if (cli.flags.bundle) {
bundle = require("./bundle").bundle;
}
async function runBatch(inputs, outputDir) {
for (let input2 of inputs) {
try {
await run(path.resolve(input2), path.join(outputDir, path.basename(input2)));
} catch (exception) {
console.error(exception);
}
}
}
if (cli.flags.outdir && cli.input.length === 1) {
run(cli.input[0], path.join(cli.flags.outdir, path.basename(cli.input[0])));
} else if (cli.flags.outdir && cli.input.length > 1) {
runBatch(cli.input, path.resolve(cli.flags.outdir));
} else if (cli.input.length === 1) {
run(cli.input[0], null);
} else if (cli.input.length === 2) {
run(cli.input[0], cli.input[1]);
}

@@ -1,25 +0,21 @@

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
var __defProp = Object.defineProperty;
var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
var __export = (target, all) => {
__markAsModule(target);
for (var name in all)
__defProp(target, name, {get: all[name], enumerable: true});
};
__export(exports, {
requireFromString: () => requireFromString
});
exports.requireFromString = requireFromString;
const path = require("path");
const Module = require("module");
const fs = require("fs");
function requireFromString(code, _filename, _require) {
let filename = _filename;
if (!_filename || path.dirname(_filename) === "" || path.dirname(_filename) === "." || _filename.startsWith(".")) {
filename = path.join(path.dirname(module.parent.id), _filename || "atbuild.tmp.js");
}
var parent = module.parent;
var paths = Module._nodeModulePaths(path.dirname(filename));
filename = path.join(path.dirname(filename), path.basename(filename, path.extname(filename)) + ".js");

@@ -30,12 +26,12 @@ var m = new Module(filename, parent);

m.paths = paths.slice().concat(parent.paths);
m.namespaceCollisionHack = function(arg) {
return arg;
};
m._compile(code, filename);
if (typeof m.exports === "function") {
let _requireAtbuild;
if (typeof _require === "function") {
_requireAtbuild = async function (id) {
const code = await _require(id);
return await requireFromString(code, id);
_requireAtbuild = async function(id) {
const code2 = await _require(id);
return await requireFromString(code2, id);
};

@@ -45,7 +41,5 @@ } else {

}
const resp = m.exports(_requireAtbuild);
if (resp.then) {
return resp.then(res => {
return resp.then((res) => {
parent && parent.children && parent.children.splice(parent.children.indexOf(m), 1);

@@ -56,3 +50,3 @@ _requireAtbuild = null;

return res;
}, err => {
}, (err) => {
_requireAtbuild = null;

@@ -74,2 +68,2 @@ _require = null;

}
}
}

@@ -1,8 +0,12 @@

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
var __defProp = Object.defineProperty;
var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
var __export = (target, all) => {
__markAsModule(target);
for (var name in all)
__defProp(target, name, {get: all[name], enumerable: true});
};
__export(exports, {
baseTypings: () => baseTypings,
generateTypings: () => generateTypings
});
exports.generateTypings = generateTypings;
exports.baseTypings = void 0;
let ts;

@@ -31,16 +35,11 @@ let baseTypings = {

};
exports.baseTypings = baseTypings;
function generateTypings(filenames, options, readFile, writeFile) {
if (!ts) {
ts = require("typescript");
} // Create a Program with an in-memory emit
}
const host = ts.createCompilerHost(options);
host.writeFile = writeFile;
host.readFile = readFile; // Prepare and emit the d.ts files
host.readFile = readFile;
const program = ts.createProgram(filenames, options, host);
return program.emit(undefined, undefined, undefined, true);
}
return program.emit(void 0, void 0, void 0, true);
}

@@ -1,7 +0,4 @@

"use strict";
const loader = require("./index");
module.exports = loader.default;
module.exports.raw = loader.raw;
module.exports.pitch = loader.pitch;
module.exports.pitch = loader.pitch;

@@ -1,22 +0,47 @@

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
var __export = (target, all) => {
__markAsModule(target);
for (var name in all)
__defProp(target, name, {get: all[name], enumerable: true});
};
var __exportStar = (target, module2, desc) => {
__markAsModule(target);
if (typeof module2 === "object" || typeof module2 === "function") {
for (let key of __getOwnPropNames(module2))
if (!__hasOwnProp.call(target, key) && key !== "default")
__defProp(target, key, {get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable});
}
return target;
};
var __toModule = (module2) => {
if (module2 && module2.__esModule)
return module2;
return __exportStar(__defProp(__create(__getProtoOf(module2)), "default", {value: module2, enumerable: true}), module2);
};
__export(exports, {
default: () => loader,
emitTypeDeclarationFile: () => emitTypeDeclarationFile,
handleESBuildResult: () => handleESBuildResult,
runWithOptions: () => runWithOptions
});
exports.default = loader;
var _atbuild = require("../atbuild");
var _esbuild = require("esbuild");
var _path = _interopRequireDefault(require("path"));
var _esbuildPlugin = _interopRequireDefault(require("../esbuildPlugin"));
var _fs = _interopRequireDefault(require("fs"));
var _generateTypings = require("../typings-plugin/generateTypings");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const atbuild = __toModule(require("../atbuild"));
const esbuild = __toModule(require("esbuild"));
const path = __toModule(require("path"));
const fullPlugin = __toModule(require("../esbuildPlugin/fullPlugin"));
const lightPlugin = __toModule(require("../esbuildPlugin/lightPlugin"));
const fs = __toModule(require("fs"));
const generateTypings = __toModule(require("../typings-plugin/generateTypings"));
const loader_utils = __toModule(require("loader-utils"));
const light = __toModule(require("../light"));
const light2 = __toModule(require("../light"));
let service;
let runCount = 0;
let build = esbuild.build;
let transform = esbuild.transform;
const schema = {

@@ -31,2 +56,20 @@ type: "object",

additionalProperties: true
},
jsExtensions: {
type: "array",
items: [
{
type: "string"
}
],
additionalItems: true
},
atBuildExtensions: {
type: "array",
items: [
{
type: "string"
}
],
additionalItems: true
}

@@ -42,8 +85,7 @@ },

sourcemap: "inline",
entryPoints: [""],
platform: "node",
resolveExtensions: [".ts", ".js", ".tsx", ".jsx", ".jsb", ".@js"],
resolveExtensions: [".ts", ".js", ".tsx", ".jsx", ".jsb", ".@js", ".atbuild"],
bundle: true,
write: false,
plugins: [_esbuildPlugin.default]
plugins: [fullPlugin.default, lightPlugin.default]
};

@@ -54,3 +96,2 @@ const stringifiedTsconfigs = {};

let readCompilationFs;
function readTemporaryAsset(name) {

@@ -63,41 +104,52 @@ if (temporaryCodeMap.has(name)) {

}
async function runBuild(esbuildInput, callback, resourcePath, addDependency, typings2, tsconfig2, outputFormat, writeFile2) {
if (runCount > 0 && !service) {
service = await esbuild.startService({
worker: true
});
build = service.build;
transform = service.transform;
process.on("beforeExit", () => {
if (service) {
service.stop();
}
});
process.on("SIGABRT", () => {
if (service) {
service.stop();
}
});
}
return build(esbuildInput).then((res) => handleESBuildResult(res, esbuildInput.outfile, callback, resourcePath, addDependency, resourcePath, typings2, tsconfig2, outputFormat, writeFile2), (err) => {
console.error(err);
debugger;
callback(err);
});
}
function formatContent(content) {
return `
// 🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖
return `// \u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}
// Auto-generated by AtBuild
// at ${Date.now()}
// 🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖
// \u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}\u{1F916}
` + content;
}
async function writeFile(name, content) {
const outExt = _path.default.extname(name.replace(".d.ts", ""));
const outExt = path.default.extname(name.replace(".d.ts", ""));
const outName = name.replace(outExt, "");
temporaryCodeMap.delete(name);
if (outExt.length === 0) {
return;
} else if (content) {
_fs.default.promises.writeFile(outName, formatContent(content), "utf-8");
} else if (_fs.default.existsSync(name)) {
_fs.default.promises.writeFile(outName, "", "utf-8");
fs.default.promises.writeFile(outName, formatContent(content), "utf-8");
} else if (fs.default.existsSync(name)) {
fs.default.promises.writeFile(outName, "", "utf-8");
}
}
async function emitTypeDeclarationFile(resourcePath, code, typings) {
async function emitTypeDeclarationFile(resourcePath, code, typings2, writeFile2) {
temporaryCodeMap.set(resourcePath + ".ts", code);
(0, _generateTypings.generateTypings)([resourcePath + ".ts"], typings, readTemporaryAsset, writeFile);
generateTypings.generateTypings([resourcePath + ".ts"], typings2, readTemporaryAsset, writeFile2);
}
async function handleESBuildResult({
outputFiles,
warnings
}, input, callback, resourcePath, addDependency, ignoreDependency, typings, tsconfig) {
async function handleESBuildResult({outputFiles, warnings}, input, callback, resourcePath, addDependency, ignoreDependency, typings2, tsconfig2, outputFormat = "esm", writeFile2) {
let source, meta;
for (let outputFile of outputFiles) {

@@ -110,3 +162,2 @@ if (outputFile.path.endsWith(input)) {

}
for (let key in meta.inputs) {

@@ -117,7 +168,5 @@ if (key !== ignoreDependency) {

}
let code;
try {
code = (0, _atbuild.requireFromString)(source, input).default;
code = atbuild.requireFromString(source, input).default;
} catch (exception) {

@@ -127,17 +176,20 @@ callback(exception);

}
if (typings) {
emitTypeDeclarationFile(resourcePath, code, typings);
const result = await (0, _esbuild.transform)(code, {
loader: "tsx",
format: "esm",
sourcefile: resourcePath + ".tsx",
sourcemap: false
});
if (typings2) {
emitTypeDeclarationFile(resourcePath, code, typings2, writeFile2);
let result;
try {
result = await transform(code, {
loader: "tsx",
format: outputFormat,
sourcefile: resourcePath + ".tsx",
sourcemap: false
});
} catch (exception) {
callback(exception);
return;
}
code = result.code;
if (process.env.WRITE_ATBUILD_TO_DISK) {
_fs.default.promises.writeFile(resourcePath.replace(_path.default.extname(resourcePath), typings ? ".out.ts" : "out.js"), code);
await fs.default.promises.writeFile(resourcePath.replace(path.default.extname(resourcePath), typings2 ? ".out.ts" : "out.js"), code);
}
callback(null, code, {

@@ -152,5 +204,4 @@ version: "3",

if (process.env.WRITE_ATBUILD_TO_DISK) {
_fs.default.promises.writeFile(resourcePath.replace(_path.default.extname(resourcePath), typings ? ".out.ts" : "out.js"), code);
await fs.default.promises.writeFile(resourcePath.replace(path.default.extname(resourcePath), typings2 ? ".out.ts" : "out.js"), code);
}
callback(null, code, {

@@ -165,57 +216,105 @@ version: "3",

}
let typings = null,
tsconfig = null,
enableTypings = false;
function loader(_code) {
if (!_code.includes("@")) {
return _code;
}
const callback = this.async();
if (this.getOptions) {
const opts = this.getOptions(schema);
if (typeof opts.tsconfig === "object") {
typings = { ...opts.tsconfig.compilerOptions,
..._generateTypings.baseTypings
};
enableTypings = true;
delete typings.moduleResolution;
if (!stringifiedTsconfigs[opts.tsconfig]) {
stringifiedTsconfigs[opts.tsconfig] = tsconfig = JSON.stringify({ ...opts.tsconfig,
compilerOptions: typings
});
let typings = null, tsconfig = null, enableTypings = false, fileExtension = "", opts;
let optionsGetter = loader_utils.getOptions;
const modes = {
light: 0,
full: 1
};
const extensionForMode = [".atbuild", ""];
const DEFAULT_JS_EXTENSIONS = [".js", ".ts", ".jsx", ".tsx"];
const DEFAULT_ATBUILD_EXTENSIONS = [".jsb", ".@js"];
function runWithOptions(_code, opts2, resourcePath, readFile, getCallback, addDependency, outputFormat, writeFile2, _mode = "auto") {
let mode = _mode;
let jsExtensions = opts2.jsExtensions || DEFAULT_JS_EXTENSIONS;
let atBuildExtensions = opts2.atBuildExtensions || DEFAULT_ATBUILD_EXTENSIONS;
fileExtension = path.default.extname(resourcePath);
switch (mode) {
case "light": {
if (light.quickTest(_code)) {
mode = modes.light;
} else {
tsconfig = stringifiedTsconfigs[opts.tsconfig];
return _code;
}
} else if (opts.typescript || opts.tsconfig) {
enableTypings = true;
typings = _generateTypings.baseTypings;
if (!stringifiedTsconfigs[_generateTypings.baseTypings]) {
stringifiedTsconfigs[_generateTypings.baseTypings] = tsconfig = JSON.stringify({
compilerOptions: typings
});
break;
}
case "full": {
mode = modes.full;
break;
}
default:
case "auto": {
if (fileExtension.length && jsExtensions.includes(fileExtension) && light.quickTest(_code)) {
mode = modes.light;
} else if (fileExtension.length && atBuildExtensions.includes(fileExtension)) {
mode = modes.full;
} else {
tsconfig = stringifiedTsconfigs[_generateTypings.baseTypings];
return _code;
}
break;
}
}
if (typeof opts2.tsconfig === "object") {
typings = {...opts2.tsconfig.compilerOptions, ...generateTypings.baseTypings};
enableTypings = true;
delete typings.moduleResolution;
if (!stringifiedTsconfigs[opts2.tsconfig]) {
stringifiedTsconfigs[opts2.tsconfig] = tsconfig = JSON.stringify({
...opts2.tsconfig,
compilerOptions: typings
});
} else {
enableTypings = false;
tsconfig = stringifiedTsconfigs[opts2.tsconfig];
}
} else if (opts2.typescript || opts2.tsconfig) {
enableTypings = true;
typings = generateTypings.baseTypings;
if (!stringifiedTsconfigs[generateTypings.baseTypings]) {
stringifiedTsconfigs[generateTypings.baseTypings] = tsconfig = JSON.stringify({
compilerOptions: typings
});
} else {
tsconfig = stringifiedTsconfigs[generateTypings.baseTypings];
}
} else {
enableTypings = false;
}
let esbuildInput = { ..._esbuildInput
let esbuildInput = {
..._esbuildInput,
format: "cjs"
};
esbuildInput.entryPoints = [this.resourcePath];
esbuildInput.outfile = this.resourcePath + ".js";
readCompilationFs = this._compilation.inputFileSystem.readFileSync;
(0, _esbuild.build)(esbuildInput).then(res => handleESBuildResult(res, esbuildInput.outfile, callback, this.resourcePath, this.addDependency, esbuildInput.entryPoints[0], enableTypings ? typings : null, tsconfig), err => {
console.error(err);
debugger;
callback(err);
});
}
switch (mode) {
case modes.full: {
esbuildInput.entryPoints = [resourcePath];
break;
}
case modes.light: {
esbuildInput.stdin = {
contents: light2.transformAST(light2.buildAST(_code), _code),
resolveDir: path.default.dirname(resourcePath),
sourcefile: path.default.basename(resourcePath) + ".js",
loader: "js"
};
esbuildInput.outfile = resourcePath + ".js";
if (!(esbuildInput.stdin.contents && esbuildInput.stdin.contents.length)) {
return _code;
}
esbuildInput.stdin.contents = esbuildInput.stdin.contents.replace("module.namespaceCollisionHack", "");
break;
}
}
readCompilationFs = readFile;
const callback = getCallback();
const result = runBuild(esbuildInput, callback, resourcePath, addDependency, enableTypings ? typings : null, tsconfig, outputFormat, writeFile2);
return result;
}
function loader(_code) {
if (this.getOptions) {
optionsGetter = this.getOptions;
}
opts = optionsGetter(schema);
const result = runWithOptions(_code, opts, this.resourcePath, this._compilation.inputFileSystem.readFileSync, this.async, this.addDependency, opts.format || "esm", opts.writeFile || writeFile);
if (typeof result === "object" && result.then) {
return;
}
return result;
}
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var __extends = exports && exports.__extends || function() {
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf || {__proto__: []} instanceof Array && function(d2, b2) {
d2.__proto__ = b2;
} || function(d2, b2) {
for (var p in b2)
if (Object.prototype.hasOwnProperty.call(b2, p))
d2[p] = b2[p];
};
return extendStatics(d, b);
};
return function(d, b) {
extendStatics(d, b);
function __() {
this.constructor = d;
}
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
}();
var __assign = exports && exports.__assign || function() {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s)
if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
exports.__esModule = true;
exports.AtBuildWebpackPlugin = void 0;
var _path = _interopRequireDefault(require("path"));
var webpack = _interopRequireWildcard(require("webpack"));
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
let loaderDir = _path.default.join(__dirname, "./index.js");
class CustomModule extends webpack.NormalModule {
constructor(mod = {}, code) {
let loaders = mod.loaders;
for (let i = 0; i < loaders.length; i++) {
var path_1 = require("path");
var webpack = require("webpack");
var loaderDir = path_1["default"].join(__dirname, "./index.js");
var CustomModule = function(_super) {
__extends(CustomModule2, _super);
function CustomModule2(mod, code) {
if (mod === void 0) {
mod = {};
}
var _this = this;
var loaders = mod.loaders;
for (var i = 0; i < loaders.length; i++) {
if (loaders[i].loader === loaderDir) {

@@ -32,4 +52,3 @@ loaders.splice(i, 1);

}
super({ ...mod,
_this = _super.call(this, __assign(__assign({}, mod), {
type: ".js",

@@ -42,99 +61,47 @@ request: mod.request + ".js",

parser: mod.parser
});
_defineProperty(this, "code", void 0);
this.code = code;
this.useSourceMap = false;
})) || this;
_this.code = code;
_this.useSourceMap = false;
if (mod.context) {
this.__source = this.createSource(mod.context, code);
_this.__source = _this.createSource(mod.context, code);
} else {
this.__source = this.createSource(this.context || "test.js", code);
_this.__source = _this.createSource(_this.context || "test.js", code);
}
return _this;
}
get _source() {
return this.__source;
}
set _source(v) {// return this.__source;
} // source(dependencyTemplates, runtimeTemplate) {}
updateHash(hash, context) {}
}
class AtBuildWebpackPlugin {
constructor(resourcePath, code) {
Object.defineProperty(CustomModule2.prototype, "_source", {
get: function() {
return this.__source;
},
set: function(v) {
},
enumerable: false,
configurable: true
});
CustomModule2.prototype.updateHash = function(hash, context) {
};
return CustomModule2;
}(webpack.NormalModule);
var AtBuildWebpackPlugin = function() {
function AtBuildWebpackPlugin2(resourcePath, code) {
this.resourcePath = resourcePath;
this.code = code;
}
apply(compiler) {
const asset = this.resourcePath;
let hasSet = false;
compiler.hooks.compilation.tap("AtBuildWebpackPlugin", (compilation, {
normalModuleFactory
}) => {
// compilation.dependencyFactories.set(
// AtBuildDependency,
// normalModuleFactory
// );
// compilation.dependencyTemplates.set(
// AtBuildDependency,
// new AtBuildDependency.Template()
// );
// let originalCreate = normalModuleFactory.create;
// normalModuleFactory.create = function (...args) {
// originalCreate(...args);
// normalModuleFactory.create(
// {
// context: asset,
// },
// new OriginalSource(this.code, asset)
// );
// compilation.addModule(module, callback);
normalModuleFactory.hooks.module.tap("AtBuildPlugin", (module, opts, arg2) => {
AtBuildWebpackPlugin2.prototype.apply = function(compiler) {
var _this = this;
var asset = this.resourcePath;
var hasSet = false;
compiler.hooks.compilation.tap("AtBuildWebpackPlugin", function(compilation, _a) {
var normalModuleFactory = _a.normalModuleFactory;
normalModuleFactory.hooks.module.tap("AtBuildPlugin", function(module2, opts, arg2) {
if (opts.resource === asset) {
const resolved = `'use strict';\n` + this.code;
var resolved = "'use strict';\n" + _this.code;
return new CustomModule(opts, resolved);
}
return module;
}); // webpack.NormalModule.getCompilationHooks(compilation).loader.tap(
// "AtBuildWebpackPlugin",
// (ctx, module) => {
// module.createLoaderContext(resolver, options, compilation, fs)
// }
// );
// compilation.hooks.succeedEntry.tap("AtBuildWebpackPlugin", (entry, a) => {
// const mod = compilation.moduleGraph.getModule(entry);
// mod._source._value = this.code;
// debugger;
// });
// compilation.hooks.moduleAsset.tap("AtBuildWebpackPlugin", (mod, str) => {
// debugger;
// });
// compilation.hooks.buildModule.tap("AtBuildWebpackPlugin", (mod) => {
// if (mod.identifier() === asset) {
// compilation.moduleGraph.updateModule(virtualDep, mod);
// debugger;
// }
// });
// compilation.hooks.finishModules.tap("AtBuildWebpackPlugin", (mod) => {
// debugger;
// });
}); // compiler.hooks.normalModuleFactory.tap("AtBuildWebpackPlugin", (nmf) => {
// // nmf.hooks.module.tap("AtBuildWebpackPlugin", (mod) => {
// // console.log(mod);
// // debugger;
// // return mod;
// // });
// });
}
}
exports.AtBuildWebpackPlugin = AtBuildWebpackPlugin;
return module2;
});
});
};
return AtBuildWebpackPlugin2;
}();
exports.AtBuildWebpackPlugin = AtBuildWebpackPlugin;
{
"name": "atbuild",
"version": "1.3.3",
"version": "1.5.0",
"main": "dist/atbuild.js",
"browser": "web/atbuild.js",
"license": "MIT",
"types": "types.ts.d",
"files": [

@@ -12,2 +13,3 @@ "./README.md",

"web",
"./types.ts.d",
"./webpack-loader.js",

@@ -24,6 +26,9 @@ "./index.js"

"@babel/preset-typescript": "^7.12.1",
"@types/benchmark": "^2.1.0",
"@types/jest": "^26.0.15",
"@types/webpack": "^4.41.24",
"babel-jest": "^26.5.2",
"babel-loader": "^8.1.0",
"jest": "^26.5.3",
"benchmark": "^2.1.4",
"jest": "^26.6.3",
"jest-cli": "^26.5.3",

@@ -33,3 +38,2 @@ "lodash": "^4.17.20",

"node-fetch": "^2.6.1",
"prettier": "^2.1.2",
"rimraf": "^3.0.2",

@@ -44,12 +48,17 @@ "typescript": "^4.0.5",

"optionalDependencies": {
"prettier": "^2.1.2",
"vscode-languageserver": "^6.1.1"
},
"scripts": {
"test": "jest",
"test": "jest src",
"bench": "node bench-data/full2-bench.js",
"cli": "yarn --silent build-node && FORCE_DEV=true node dist/cli.js $1",
"clear-test": "rm samples/*.d.ts",
"update-readme": "node dist/cli.js ./README.md.jsb ./README.md",
"build-node": "babel src --extensions \".ts\" --extensions \".js\" --ignore=./**/*.test.js -d dist --delete-dir-on-start",
"build": "yarn build-node && yarn build-web",
"build-web": "esbuild --define:\"process.env.WEB=true\" --bundle ./src/atbuild.js --format=esm --outdir=./web ",
"prebuild-node": "rimraf dist",
"prebuild-web": "rimraf web",
"build-node": "esbuild --target=node10 --define:\"process.env.NODE_ENV\"=\"'production'\" --define:\"process.env.WEB=false\" src/*{.ts,.js} src/**/*{.ts,.js} --format=cjs --outdir=./dist --platform=node",
"postbuild": "rimraf dist/**/*{.test.ts, .test.js, .ts.d.js}",
"build": "yarn build-node && yarn build-web; yarn build-types",
"build-web": "esbuild --sourcemap --define:\"process.env.NODE_ENV\"=\"'production'\" --define:\"process.env.WEB=true\" --bundle ./src/atbuild.ts ./src/light.ts --format=esm --outdir=./web ",
"build-types": "yarn tsc --p tsconfig.json --outfile types || true",
"prepublishOnly": "yarn build",

@@ -63,5 +72,7 @@ "compile-vscode-client": "babel --config-file=./babel.config.js --extensions \".ts\" ./atbuild-vscode/client/src --ignore=atbuild-vscode/**/*.test.js -d ./atbuild-vscode/client/out --delete-dir-on-start",

"dependencies": {
"atbuild": "../",
"esbuild": "https://github.com/Jarred-Sumner/esbuild/releases/download/pluginbuild/esbuild-0.8.1.tgz",
"loader-utils": "^2.0.0",
"meow": "^7.1.1"
}
}

@@ -1,8 +0,8 @@

# AtBuild – JavaScript Preprocessor
# AtBuild – Experimental JavaScript Preprocessor
AtBuild is a JavaScript preprocessor. It lets you write JavaScript that writes JavaScript.
AtBuild is a programmable code generation tool for JavaScript. It lets you write JavaScript that writes JavaScript.
Use it for:
- Easy, editable code generation
- Easy, editable code generation with full TypeScript support
- Write high-performance JavaScript libraries by removing the runtime

@@ -12,90 +12,276 @@ - Determinstic dead code elimination

Try the playground: https://atbuild.vercel.app/bundle/date-formatter.tsb
# How it works
AtBuild has two rules:
There are two flavors of AtBuild.
1. Any line that starts with `@` will be evaluated at buildtime instead of runtime.
1. AtBuild Light: compatible with current JavaScript syntax
2. AtBuild Full: a powerful JavaScript-based templating language for generating code. It's close to but not quite JavaScript, which is why it has it's own file extension: `.jsb`/`.tsb`
2. Any line containing `@{codeInHere}` will be evaluated at buildtime instead of runtime.
### Atbuild Light
You write some of your JavaScript in `.@js` files, and by default, all the code in the file will be evaluated at runtime.
Atbuild Light preprocesses your JavaScript & TypeScript files by setting three conventions:
But, if the line starts with an `@` or if it contains `@{}`, those parts of the file will be switched out, and run at buildtime instead.
1. Code inside of `$(buildTimeCodeInHere)` will be run & replaced at buildtime (❤️ jQuery)
2. Code fenced within `// $$` will be moved to buildtime
3. Lines ending with `// $` with be moved to buildtime
The code evaluated at buildtime is also JavaScript.
#### Small exmaple
## Contrived example:
`input.js`:
```js
// hello-world.@js
@var hi = 0;
import { $ } from "atbuild";
@for (let i = 0; i < 5; i++) {
console.log("Hello World @{i}");
@hi++;
@}
// $$
module.exports = @{hi};
const didRemoveBuildTimeCode = false;
// $$-
export const isRemoved = $(!didRemoveBuildTimeCode);
```
After we run it through `atbuild ./hello-world.@js`, it becomes:
⌨️ `atbuild ./input.js ./output.js`
`output.js`:
```js
// hello-world.js
const isRemoved = true;
export { isRemoved };
```
console.log("Hello World 0");
console.log("Hello World 1");
console.log("Hello World 2");
console.log("Hello World 3");
console.log("Hello World 4");
Note: the `import {$}` is there for convience so your editor doesn't get mad. Any function call starting with `$` is assumed to be a build-time function.
module.exports = 5;
Unlike other buildtime code generation tools, you can `import` from `node_modules`, and even import other modules in your codebase (so long as it runs without a `window` object). The input is transformed using `esbuild`.
`input.js`:
```ts
import { $createDateFormatter } from "atbuild/demo/date-formatter"; // $
export const formatTime = $createDateFormatter("hh:mm:ss");
```
## Changelog
⌨️ `atbuild ./input.js ./output.js`
**October 30th, 2020**: Added support for nested buildtime modules to export functions that are only available at buildtime. This allows you to write zero-runtime libraries.
`output.js`:
**October 30th, 2020**: Added support for nested buildtime modules in the webpack-loader, so you can import @js files from inside @js files and it will work as expected (buildtime code is executed, runtime code is generated)
```js
export const formatTime = function (date: Date) {
let formattedDate = "";
var hours = date.getUTCHours() % 12;
hours = hours || 12;
formattedDate += hours.toString(10).padStart(2, "0");
formattedDate += ":";
formattedDate += date.getUTCMinutes().toString(10).padStart(2, "0");
formattedDate += ":";
formattedDate += date.getUTCSeconds().toString(10).padStart(2, "0");
return formattedDate;
};
```
**October 29th, 2020**: Added support for bundling buildtime code in the webpack loader, meaning you can use the same syntax for buildtime code and runtime code. This also makes it easy to import runtime modules at buildtime. The webpack-loader uses [esbuild](https://esbuild.github.io/) for bundling the backend code.
And it supports types.
**October 28th, 2020**: Extremely WIP VSCode extension.
For compatibility reasons, exporting build time code from JavaScript/TypeScript outside of the file is not supported. But, that's why there's Atbuild Full, which lets you write libraries for proceedurally generating code at build time.
**October 28th, 2020**: `await` is now supported for buildtime code (not in webpack)
### Atbuild Full
**October 28th, 2020**: New syntax: `@@` allows multiline buildtime code generation.
Atbuild Full adds a few new keywords to JavaScript. It lets you evaluate & generate code at build time using JavaScript.
For example:
1. `@build`: code contained inside `@build` will be run at build-time instead of runtime
```java
// The code inside @@ is run at build-time.
@@
const fetch = require("node-fetch")
const resp = await fetch("https://github.com/Jarred-Sumner/atbuild/commit/master.patch")
const text = await resp.text()
@@
```js
@build
const yourBrowserDoesntKnowAboutThisCode = true;
@end
```
2. `@run`: code contained inside `@run` will be included at runtime.
// At buildtime, `@{text}` is replaced with the output from https://github.com/Jarred-Sumner/atbuild/commit/master.patch.
module.exports = `@{text}`
```js
@run
console.log("This code will be included at runtime");
@end
```
**October 28th, 2020**: Added support for `require` in buildtime code. Runtime code works like normal and is run through Babel or any other loaders you use. ~Buildtime code isn't run through babel, but this might be implemented later via webpack's `this._compilation_.createChildCompiler`, which would run buildtime and runtime code both through webpack.~ Fixed
3. `@run` and `@build` can be nested. `@()` is like string interpolation but for generating code.
For example:
```js
@build
// This for loop isn't included in the runtime code.
for (let i = 0; i < 3;i++) {
@run
console.log("This code will be included at runtime @(i)");
@end
}
@end
```
```java
// The code inside @@ is run at build-time.
@@
const fetch = require("node-fetch")
const resp = await fetch("https://github.com/Jarred-Sumner/atbuild/commit/master.patch")
const text = await resp.text()
@@
And this is the output:
```js
console.log("This code will be included at runtime 0");
console.log("This code will be included at runtime 1");
console.log("This code will be included at runtime 2");
```
// At buildtime, `@{text}` is replaced with the output from https://github.com/Jarred-Sumner/atbuild/commit/master.patch.
module.exports = `@{text}`
4. `@export function $FunctionNameGoesHere(arguments, in, here)` adds a build-time exported function that can be called from regular JavaScript/TypeScript files. Before it reaches the browser, the function call is replaced with the code generated from the function call.
You write some of your JavaScript in `.jsb` files, and by default, all the code in the file will be evaluated at runtime.
The code evaluated at buildtime is also JavaScript.
All of this works with your bundler, so you can import React components and generates type definitions.
## Contrived example:
```js
// contrived-api-endpoint-codegenerator.jsb.
@build
import { kebabCase, startCase, toLower} from 'lodash';
const titleize = str => startCase(toLower(str));
const BASE_URL = `http://example.com`;
@end
type BaseType = {
id: number;
}
@build
for (let objectName of ["Post", "User", "Like", "PasswordResetToken"]) {
@run
export type @(objectName) = BaseType & {
object: "@(kebabCase(objectName))";
@build
switch(objectName) {
case "PasswordResetToken": {
@run
used: boolean;
expiry: Date;
@end
}
}
@end
}
export function build@(objectName)FromJSON(json: Object): @(objectName) {
return json;
}
export async function fetch@(objectName)ById(id: number): Promise<@(objectName)> {
@build
var base = BASE_URL + `/${kebabCase(objectName)}s/`;
@end
const body = (await fetch("@(base)" + id)).body()
const json = await body.json()
return build@(objectName)FromJSON(json);
}
@end
}
@end
```
After we run it through `atbuild ./contrived-api-endpoint-codegenerator.jsb`, it becomes:
```js
// contrived-api-endpoint-codegenerator.js
function buildPostFromJSON(json) {
return json;
}
async function fetchPostById(id) {
const body = (await fetch("http://example.com/posts/" + id)).body();
const json = await body.json();
return buildPostFromJSON(json);
}
function buildUserFromJSON(json) {
return json;
}
async function fetchUserById(id) {
const body = (await fetch("http://example.com/users/" + id)).body();
const json = await body.json();
return buildUserFromJSON(json);
}
function buildLikeFromJSON(json) {
return json;
}
async function fetchLikeById(id) {
const body = (await fetch("http://example.com/likes/" + id)).body();
const json = await body.json();
return buildLikeFromJSON(json);
}
function buildPasswordResetTokenFromJSON(json) {
return json;
}
async function fetchPasswordResetTokenById(id) {
const body = (
await fetch("http://example.com/password-reset-tokens/" + id)
).body();
const json = await body.json();
return buildPasswordResetTokenFromJSON(json);
}
export {
buildLikeFromJSON,
buildPasswordResetTokenFromJSON,
buildPostFromJSON,
buildUserFromJSON,
fetchLikeById,
fetchPasswordResetTokenById,
fetchPostById,
fetchUserById,
};
```
This also generates a `contrived-api-endpoint-codegenerator.ts.d` file:
```ts
declare type BaseType = {
id: number;
};
export declare type Post = BaseType & {
object: "post";
};
export declare function buildPostFromJSON(json: Object): Post;
export declare function fetchPostById(id: number): Promise<Post>;
export declare type User = BaseType & {
object: "user";
};
export declare function buildUserFromJSON(json: Object): User;
export declare function fetchUserById(id: number): Promise<User>;
export declare type Like = BaseType & {
object: "like";
};
export declare function buildLikeFromJSON(json: Object): Like;
export declare function fetchLikeById(id: number): Promise<Like>;
export declare type PasswordResetToken = BaseType & {
object: "password-reset-token";
used: boolean;
expiry: Date;
};
export declare function buildPasswordResetTokenFromJSON(
json: Object
): PasswordResetToken;
export declare function fetchPasswordResetTokenById(
id: number
): Promise<PasswordResetToken>;
export {};
```
## Changelog
**November 6th**: New syntax for Atbuild Full, and a new parser to go with it.
**October 30th, 2020**: Added support for nested buildtime modules to export functions that are only available at buildtime. This allows you to write zero-runtime libraries.
**October 30th, 2020**: Added support for nested buildtime modules in the webpack-loader, so you can import jsb files from inside jsb files and it will work as expected (buildtime code is executed, runtime code is generated)
**October 29th, 2020**: Added support for bundling buildtime code in the webpack loader, meaning you can use the same syntax for buildtime code and runtime code. This also makes it easy to import runtime modules at buildtime. The webpack-loader uses [esbuild](https://esbuild.github.io/) for bundling the backend code.
**October 28th, 2020**: Extremely WIP VSCode extension.
**October 28th, 2020**: Added support for `require` in buildtime code. Runtime code works like normal and is run through Babel or any other loaders you use. ~Buildtime code isn't run through babel, but this might be implemented later via webpack's `this._compilation_.createChildCompiler`, which would run buildtime and runtime code both through webpack.~ Fixed

@@ -105,3 +291,3 @@

<img alt="Y U Do Dis meme" src="./explain/y.png" height=120 />
<img alt="Y U Do Dis meme" src="./explain/y.png" height="120" />

@@ -148,11 +334,11 @@ Extremely fast native languages like Rust & C often use [inline expansion](https://en.wikipedia.org/wiki/Inline_expansion) and [loop unrolling](https://en.wikipedia.org/wiki/Loop_unrolling) to move work from runtime to buildtime. For code that doesn't change much, this can be a massive performance improvement.

```bash
atbuild ./input.@js
atbuild ./input.jsb
```
```bash
atbuild ./input.@js ./output.js
atbuild ./input.jsb ./output.js
```
```bash
atbuild ./input.@js ./output.js --pretty --no-header
atbuild ./input.jsb ./output.js --pretty --no-header
```

@@ -162,5 +348,5 @@

**The recommended way to use AtBuild is through the Webpack loader**. This configures Webpack to run any file that ends in `.@js` through AtBuild automatically.
**The recommended way to use AtBuild is through the Webpack loader**
Buildtime code is run through a [high performance bundler](https://esbuild.github.io/) for you automatically, so you can write your buildtime code using the same modern JavaScript as the rest of your code. This also means you can import other modules, and those modules don't have to be `.@js` files - they can be any other file in your codebase (so long as it runs in Node after bundling).
Buildtime code is run through a [high performance bundler](https://esbuild.github.io/) for you automatically, so you can write your buildtime code using the same modern JavaScript as the rest of your code. This also means you can import other modules, and those modules don't have to be `.jsb` files - they can be any other file in your codebase (so long as it runs in Node after bundling).

@@ -178,17 +364,15 @@ Runtime code is passed through webpack as regular JavaScript – so you can still use babel-loader as normal.

// AtBuild.js Webpack Loader
// AtBuild.js Webpack Loader
{
// File extension is .@js
test: /\.@js$/,
test: /\.(jsb|js|ts|tsx|jsx|@js)$/,
exclude: /node_modules/,
type: "javascript/auto",
enforce: "pre",
use: [
{
loader: "atbuild/webpack-loader
loader: "atbuild/webpack-loader,
options: {
// Generate a .d.ts file automatically so your IDE can more easily interop with AtBuild files.
typescript: true,
}
},
// Run Babel on runtime code afterwards (optional)
{
loader: "babel-loader",
options: {/* your babel options in here if relevant */},
},
]

@@ -228,3 +412,3 @@ },

config.module.rules.unshift({
test: /\.@js$/,
test: /\.jsb$/,
use: [

@@ -231,0 +415,0 @@ // Pass the loader in before atbuild, so that atbuild runs first.g

@@ -16,16 +16,13 @@ var __defProp = Object.defineProperty;

// src/atbuild.js
// src/atbuild.ts
var require_atbuild = __commonJS((exports, module) => {
__export(exports, {
AtBuild: () => AtBuild,
default: () => atbuild_default
buildAST: () => buildAST,
default: () => $,
requireFromString: () => requireFromString,
transformAST: () => transformAST
});
let fs;
const BUILD_TIME_MATCHER = /^\s*@(.*)/;
const MULTILINE_BUILD_TIME_MATCHER = /^\s*@@(.*)/;
const RUNTIME_MATCHER = /\@\{([^@}]*)\}/gm;
const HEADER_STRING = '/* eslint-disable */\n// @ts-nocheck\n// @ts-ignore\n// @noflow\n"use strict";\n\n';
const getMaxLine = function(currentLine, node) {
return Math.max(currentLine, node.lineNumber);
};
let requireFromString;

@@ -40,3 +37,3 @@ if (true) {

} else {
requireFromString = null;
requireFromString = null.requireFromString;
fs = null;

@@ -46,181 +43,4 @@ }

static buildAST(code) {
const nodes = [];
let lineMatch = null;
let lines = String(code).split("\n");
let isMultiline = false;
for (let i = 0; i < lines.length; i++) {
if (lines[i].trim().length === 0) {
continue;
}
if (lineMatch = lines[i].match(MULTILINE_BUILD_TIME_MATCHER)) {
if (isMultiline) {
isMultiline = false;
} else {
isMultiline = true;
}
let scopeValue = 0;
if (lineMatch[1].trimEnd().endsWith("{")) {
scopeValue++;
} else if (lineMatch[1].trimEnd().endsWith("{")) {
scopeValue--;
}
let string = lineMatch[1];
for (let i2 = 0; i2 < lineMatch[0].indexOf(lineMatch[1]) + 1; i2++) {
string = " " + string;
}
nodes.push({
lineNumber: i,
type: "MultilineBuildtimeCode",
value: string,
scope: scopeValue
});
} else if (isMultiline) {
nodes.push({
lineNumber: i,
type: "MultilineBuildtimeCode",
value: lines[i]
});
} else if (lineMatch = lines[i].match(BUILD_TIME_MATCHER)) {
let scopeValue = 0;
if (lineMatch[1].trimEnd().endsWith("{")) {
scopeValue++;
} else if (lineMatch[1].trimEnd().endsWith("{")) {
scopeValue--;
}
let string = lineMatch[1];
for (let i2 = 0; i2 < lineMatch[0].indexOf(lineMatch[1]) + 1; i2++) {
string = " " + string;
}
nodes.push({
lineNumber: i,
type: "BuildtimeCode",
value: string,
scope: scopeValue
});
} else {
let line = [
{
lineNumber: i,
type: "RuntimecodeLineStart"
},
{
lineNumber: i,
type: "RuntimeCode",
value: lines[i],
column: 0
}
];
let result;
let lineToMatch = lines[i];
let offset = 0;
for (let result2 of lineToMatch.matchAll(RUNTIME_MATCHER)) {
const [input, match] = result2;
const index = result2.index;
const original = line[line.length - 1].value;
line[line.length - 1].value = original.substring(0, index - offset);
line.length += 2;
line[line.length - 2] = {
lineNumber: i,
type: "InterpolatedCode",
value: match,
column: index - offset
};
lineToMatch = result2.input.substring(offset = index + input.length);
line[line.length - 1] = {
type: "RuntimeCode",
lineNumber: i,
value: lineToMatch,
column: offset
};
}
nodes.push(...line);
nodes.push({
lineNumber: i,
type: "RuntimecodeLineEnd"
});
}
}
return nodes;
return buildAST(code);
}
static transformASTBuildTimeOnly(nodes) {
const maxLineNumber = nodes.reduce(getMaxLine, 0);
let lines = new Array(maxLineNumber + 1);
for (let i = 0; i < lines.length; i++) {
lines[i] = "";
}
for (let node of nodes) {
switch (node.type) {
case "BuildtimeCode": {
lines[node.lineNumber] += node.value;
if (!lines[node.lineNumber].endsWith("\n")) {
lines[node.lineNumber] += "\n";
}
break;
}
case "InterpolatedCode": {
lines[node.lineNumber] += "${" + node.value + "}";
break;
}
case "RuntimeCode": {
lines[node.lineNumber] += node.value;
break;
}
case "RuntimecodeLineStart": {
lines[node.lineNumber] += "`";
break;
}
case "RuntimecodeLineEnd": {
lines[node.lineNumber] += "`;\n";
break;
}
}
}
return lines.join("");
}
static transformAST(nodes, asFunction) {
let code;
if (asFunction) {
code = "module.exports.default = async function __atBuild(require) { var __CODE__ = [];\n\n";
} else {
code = "var __CODE__ = [];\n\n";
}
const maxLineNumber = nodes.reduce(getMaxLine, 0);
let lines = new Array(maxLineNumber + 3);
for (let i = 0; i < lines.length; i++) {
lines[i] = "";
}
for (let node of nodes) {
switch (node.type) {
case "MultilineBuildtimeCode":
case "BuildtimeCode": {
lines[node.lineNumber] += node.value + "\n";
break;
}
case "InterpolatedCode": {
lines[node.lineNumber] += "${" + node.value + "}";
break;
}
case "RuntimeCode": {
lines[node.lineNumber] += node.value.replace(/`/igm, "\\`");
break;
}
case "RuntimecodeLineStart": {
lines[node.lineNumber] += "__CODE__.push(`";
break;
}
case "RuntimecodeLineEnd": {
lines[node.lineNumber] += "`);\n";
break;
}
}
}
lines.unshift(code);
if (asFunction) {
lines[lines.length - 1] = `return __CODE__.join("\\n");
}; module.exports.__specialInitFunction = true;`;
} else {
lines[lines.length - 1] = `module.exports.default = __CODE__.join("\\n");`;
}
return lines.join("");
}
static *findNodesAtLine(nodes, lineNumber) {

@@ -234,89 +54,2 @@ for (let i = 0; i < nodes.length; i++) {

}
static ASTResponseType = {
BuildtimeCode: 0,
RuntimeCode: 1
};
static transformASTForLineColumn(nodes, lineNumber, column, response) {
let lineNode = this.findNodesAtLine(nodes, lineNumber).next().value;
if (!lineNode) {
response[0] = "";
response[1] = this.ASTResponseType.RuntimeCode;
response[2] = lineNumber;
response[3] = column;
return;
}
if (lineNode.type === "BuildtimeCode" || lineNode.type === "InterpolatedCode") {
response[0] = this.transformASTBuildTimeOnly(nodes);
response[1] = this.ASTResponseType.BuildtimeCode;
response[2] = lineNumber;
response[3] = column;
return;
}
let code = "var __CODE__ = [];\n\n";
const maxLineNumber = nodes.reduce(getMaxLine, 0);
let lines = new Array(maxLineNumber + 3);
for (let i = 0; i < lines.length; i++) {
lines[i] = "";
}
let lineOffset = 0;
for (let node of nodes) {
switch (node.type) {
case "BuildtimeCode": {
lines[node.lineNumber] += node.value + "\n";
lineOffset++;
break;
}
case "InterpolatedCode": {
lines[node.lineNumber] += "${" + node.value + "}";
break;
}
case "RuntimeCode": {
lines[node.lineNumber] += `/* ATBuildColumnMap: ${node.column} */` + node.value;
break;
}
case "RuntimecodeLineStart": {
lines[node.lineNumber] += `__CODE__.push(\`/* ATBuildLineMap: ${node.lineNumber} */`;
break;
}
case "RuntimecodeLineEnd": {
lines[node.lineNumber] += "`);\n";
break;
}
}
}
lines.unshift(code);
lines[lines.length - 1] = `module.exports = __CODE__.join("\\n");`;
response[0] = lines.join("\n");
response[1] = this.ASTResponseType.RuntimeCode;
response[2] = lineNumber;
response[3] = column;
return;
}
static extractSourceAndType(code, filepath, line, column, response) {
const ast = AtBuild.buildAST(code);
response[2] = response[3] = 0;
AtBuild.transformASTForLineColumn(ast, line, column, response);
if (response[0] !== "" && response[1] === this.ASTResponseType.RuntimeCode) {
const source = this._eval(response[0], filepath, false);
const lines = source.split("\n");
for (let i = 0; i < lines.length; i++) {
if (lines[i].indexOf(`/* AtBuildLineMap: ${line} */`) > -1) {
response[2] = i;
for (let offset = 0; offset < lines[i]; offset++) {
const line2 = lines[i].substring(offset);
let _offset = line2.indexOf(`/* AtBuildColumnMap: ${column} */`);
let match = null;
if (_offset > -1) {
response[3] = offset + _offset;
break;
} else if (match = line2.match(/\/\* AtBuildColumnMap: (\d*) \*\//)) {
}
}
response[3] = lines[i].indexOf(`/* AtBuildColumnMap: ${column} */`);
break;
}
}
response[0] = source.replace(/\/\* AtBuildColumnMap: \d* \*\//gim, "").replace(/\/\* AtBuildLineMap: \d* \*\//gim, "");
}
}
static evalFile(path, header) {

@@ -332,5 +65,2 @@ return this.eval(fs.readFileSync(path), path, header, module.parent);

source = HEADER_STRING + source;
source += `
module.exports = __atBuild
`;
}

@@ -341,3 +71,3 @@ return source;

const ast = AtBuild.buildAST(code);
const processed = AtBuild.transformAST(ast, false);
const processed = AtBuild.transformAST(ast, code);
const res = this._eval(processed, filepath, addHeader, requireFunc);

@@ -352,3 +82,3 @@ if (res && res.default) {

const ast = AtBuild.buildAST(code);
const processed = AtBuild.transformAST(ast, true);
const processed = AtBuild.transformAST(ast, code);
let source = await requireFromString(processed, filepath, requireFunc);

@@ -361,4 +91,630 @@ if (addHeader) {

}
var atbuild_default = AtBuild;
AtBuild.transformAST = transformAST;
AtBuild.ASTResponseType = {
BuildtimeCode: 0,
RuntimeCode: 1
};
function $(arg) {
return arg;
}
});
// src/fullAst.ts
var CharacterType;
(function(CharacterType2) {
CharacterType2[CharacterType2["ignore"] = 0] = "ignore";
CharacterType2[CharacterType2["newline"] = 13] = "newline";
CharacterType2[CharacterType2["whitespace"] = 1] = "whitespace";
CharacterType2[CharacterType2["alphanumeric"] = 3] = "alphanumeric";
CharacterType2[CharacterType2["control"] = 4] = "control";
CharacterType2[CharacterType2["scopeOpener"] = 5] = "scopeOpener";
CharacterType2[CharacterType2["scopeCloser"] = 6] = "scopeCloser";
CharacterType2[CharacterType2["variableMapOpener"] = 7] = "variableMapOpener";
CharacterType2[CharacterType2["variableMapCloser"] = 8] = "variableMapCloser";
CharacterType2[CharacterType2["variableMapSeparator"] = 9] = "variableMapSeparator";
CharacterType2[CharacterType2["inlineOpener"] = 10] = "inlineOpener";
CharacterType2[CharacterType2["inlineCloser"] = 11] = "inlineCloser";
CharacterType2[CharacterType2["escape"] = 12] = "escape";
CharacterType2[CharacterType2["replacerStart"] = 2] = "replacerStart";
})(CharacterType || (CharacterType = {}));
var Scope;
(function(Scope2) {
Scope2[Scope2["none"] = 0] = "none";
Scope2[Scope2["inline"] = 1] = "inline";
Scope2[Scope2["multiline"] = 2] = "multiline";
})(Scope || (Scope = {}));
var ParseOperation;
(function(ParseOperation2) {
ParseOperation2[ParseOperation2["findControl"] = 0] = "findControl";
ParseOperation2[ParseOperation2["determineKeyword"] = 1] = "determineKeyword";
ParseOperation2[ParseOperation2["determineKeywordAttribute"] = 2] = "determineKeywordAttribute";
ParseOperation2[ParseOperation2["closeVariableMap"] = 3] = "closeVariableMap";
ParseOperation2[ParseOperation2["closeInline"] = 4] = "closeInline";
ParseOperation2[ParseOperation2["determineReplacer"] = 5] = "determineReplacer";
ParseOperation2[ParseOperation2["closeScope"] = 6] = "closeScope";
ParseOperation2[ParseOperation2["determineName"] = 7] = "determineName";
ParseOperation2[ParseOperation2["closeName"] = 8] = "closeName";
})(ParseOperation || (ParseOperation = {}));
var ASTNodeKeyword;
(function(ASTNodeKeyword2) {
ASTNodeKeyword2[ASTNodeKeyword2["source"] = 0] = "source";
ASTNodeKeyword2[ASTNodeKeyword2["run"] = 1] = "run";
ASTNodeKeyword2[ASTNodeKeyword2["build"] = 2] = "build";
ASTNodeKeyword2[ASTNodeKeyword2["export"] = 3] = "export";
ASTNodeKeyword2[ASTNodeKeyword2["inline"] = 4] = "inline";
ASTNodeKeyword2[ASTNodeKeyword2["replacer"] = 5] = "replacer";
ASTNodeKeyword2[ASTNodeKeyword2["root"] = 6] = "root";
ASTNodeKeyword2[ASTNodeKeyword2["interpolate"] = 7] = "interpolate";
})(ASTNodeKeyword || (ASTNodeKeyword = {}));
let astNodeBase = {
children: [],
variableMapping: [],
scope: 0,
keyword: 0,
name: "",
value: "",
functionDeclarationSuffix: "",
lineStart: 0,
lineEnd: 0,
colStart: 0,
colEnd: 0,
from: 0,
to: 0
};
const ScopeNames = {
[1]: "inline",
[0]: null,
[2]: "multiline"
};
const KeywordName = {
[0]: "source",
[1]: "run",
[2]: "build",
[3]: "export",
[4]: "inline",
[5]: "$",
[6]: "root"
};
const _toJSON = (item) => item.toJSON();
astNodeBase.toJSON = function() {
const {
parent,
_parent,
scope,
keyword,
children,
colStart,
lineStart,
lineEnd,
colEnd,
...json
} = this;
return {
...json,
children: children.map(_toJSON),
scope: ScopeNames[scope],
keyword: KeywordName[keyword],
column: {
start: colStart,
end: colEnd
},
line: {
start: lineStart,
end: lineEnd
}
};
};
Object.defineProperty(astNodeBase, "parent", {
get() {
return this._parent && this._parent.deref();
},
set(parent) {
if (parent) {
return this._parent = new WeakRef(parent);
} else {
return this._parent = null;
}
}
});
if (false) {
Object.defineProperty(astNodeBase, "k", {
get() {
return ASTNodeKeyword[this.keyword];
}
});
astNodeBase.original = function(source) {
return source.substring(this.from, this.to);
};
}
const charTypes = new Uint8Array(255);
const emptyCharTypes = new Uint8Array(255);
const incrementLineNumber = new Uint8Array(255);
var ControlIdentifier;
(function(ControlIdentifier2) {
ControlIdentifier2[ControlIdentifier2["invalid"] = 0] = "invalid";
ControlIdentifier2[ControlIdentifier2["inline"] = 1] = "inline";
ControlIdentifier2[ControlIdentifier2["export"] = 2] = "export";
ControlIdentifier2[ControlIdentifier2["build"] = 3] = "build";
ControlIdentifier2[ControlIdentifier2["run"] = 4] = "run";
ControlIdentifier2[ControlIdentifier2["closeScope"] = 5] = "closeScope";
ControlIdentifier2[ControlIdentifier2["interpolate"] = 6] = "interpolate";
})(ControlIdentifier || (ControlIdentifier = {}));
const Keywords = {
run: {
start: "run",
scope: true,
inline: true,
variableMapper: true,
name: false,
arguments: false,
prefixCode: "r".charCodeAt(0)
},
build: {
start: "build",
scope: true,
inline: true,
variableMapper: true,
name: false,
arguments: false,
prefixCode: "b".charCodeAt(0)
},
export: {
start: "export",
scope: true,
inline: false,
variableMapper: false,
name: true,
arguments: true,
prefixCode: "e".charCodeAt(0)
},
inline: {
start: "inline",
scope: false,
inline: true,
variableMapper: true,
name: false,
arguments: false,
prefixCode: "i".charCodeAt(0)
}
};
const controlIdentifierTypes = new Uint8Array(255);
const controlIdentifierSkipLength = new Uint8Array(8);
const operationsByControlIdentifier = new Uint8Array(8);
const keywordNames = new Array(6);
function getControlIdentifier(code, position) {
if (code[position + 1] === "e" && code[position + 2] === "n") {
return 5;
} else {
return controlIdentifierTypes[code.charCodeAt(position + 1)];
}
}
emptyCharTypes.fill(0);
emptyCharTypes[1] = 1;
emptyCharTypes[13] = 1;
controlIdentifierTypes[Keywords.inline.prefixCode] = 1;
controlIdentifierTypes[Keywords.run.prefixCode] = 4;
controlIdentifierTypes[Keywords.build.prefixCode] = 3;
controlIdentifierTypes[Keywords.export.prefixCode] = 2;
controlIdentifierTypes["(".charCodeAt(0)] = 6;
controlIdentifierSkipLength[1] = "inline".length;
controlIdentifierSkipLength[4] = "run".length;
controlIdentifierSkipLength[3] = "build".length;
controlIdentifierSkipLength[6] = "(".length;
controlIdentifierSkipLength[2] = "export function".length;
controlIdentifierSkipLength[5] = "end".length;
keywordNames[1] = "inline";
keywordNames[4] = "run";
keywordNames[3] = "build";
keywordNames[2] = "export function";
keywordNames[5] = "end";
keywordNames[6] = "(";
operationsByControlIdentifier.fill(2);
operationsByControlIdentifier[2] = 7;
operationsByControlIdentifier[6] = 4;
const keywordTypes = new Uint8Array(8);
keywordTypes[1] = 4;
keywordTypes[4] = 1;
keywordTypes[3] = 2;
keywordTypes[6] = 7;
keywordTypes[2] = 3;
incrementLineNumber[13] = 1;
const backtrackAmount = new Int8Array(16);
backtrackAmount[13] = -1;
backtrackAmount[1] = -1;
for (let code = 0; code < 256; code++) {
if (code > 64 && code < 91 || code > 96 && code < 123) {
charTypes[code] = 3;
} else if (code === "$".charCodeAt(0)) {
charTypes[code] = 2;
} else if (code === "\n".charCodeAt(0)) {
charTypes[code] = 13;
} else if (code === " ".charCodeAt(0)) {
charTypes[code] = 1;
} else if (code === "@".charCodeAt(0)) {
charTypes[code] = 4;
} else if (code === "(".charCodeAt(0)) {
charTypes[code] = 10;
} else if (code === ")".charCodeAt(0)) {
charTypes[code] = 11;
} else if (code === "<".charCodeAt(0)) {
charTypes[code] = 7;
} else if (code === ",".charCodeAt(0)) {
charTypes[code] = 9;
} else if (code === ">".charCodeAt(0)) {
charTypes[code] = 8;
} else {
}
}
var ParseErrorType;
(function(ParseErrorType2) {
ParseErrorType2[ParseErrorType2["invalidKeyword"] = 0] = "invalidKeyword";
ParseErrorType2[ParseErrorType2["invalidExportFunction"] = 1] = "invalidExportFunction";
ParseErrorType2[ParseErrorType2["strayOpenBrace"] = 2] = "strayOpenBrace";
})(ParseErrorType || (ParseErrorType = {}));
const ParseErrorNames = {
[2]: "Invalid {",
[0]: "Invalid keyword",
[1]: "Invalid export function"
};
class AtbuildParseError extends Error {
constructor(type, name, message) {
super(message);
this.name = name;
this.type = type;
}
}
function buildAST(code, filename = "file.tsb") {
const root = Object.create(astNodeBase);
let sourceNode;
let position = 0, cursor = 0, operation = 0, controlIdentifierType = 0, prevCursor = cursor, line = 0, column = 0, skipLength = 0, parent = root, replacerNode, keywordNode, inlineDepthCount = 0, scopeDepthCount = 0, inlineStart = 0, nameStart = 0, variableMapOpenerStart = 0, variableMapArgumentStart = 0, lastNode, endOfPreviousLine = 0, endOfPreviousLineColumn = 0, isLineEmpty = 1, inlineEnd = 0, replacerStart = 0;
root.children = [];
root.keyword = 6;
for (position = 0; position < code.length; position++, prevCursor = cursor) {
cursor = charTypes[code.charCodeAt(position)];
if (incrementLineNumber[cursor]) {
endOfPreviousLine = position - 1;
endOfPreviousLineColumn = column;
line++;
column = -1;
isLineEmpty = 1;
} else {
}
isLineEmpty = Math.min(isLineEmpty, emptyCharTypes[cursor]);
column++;
if (operation === 0 && cursor === 4) {
controlIdentifierType = getControlIdentifier(code, position);
skipLength = controlIdentifierSkipLength[controlIdentifierType] | 0;
if (controlIdentifierType === 0 || keywordNames[controlIdentifierType] !== code.substring(position + 1, position + skipLength + 1)) {
throw new AtbuildParseError(0, `Invalid @ keyword in ${filename}:${line}:${column - 1}`, `Must be @run, @build, @export function $, @inline, @(buildCode), or @end. Received "${code.substring(position).split(" ")[0].slice(0, 10).replace("\n", "\\n")}"
`);
} else if (controlIdentifierType === 5) {
keywordNode.to = position;
keywordNode.lineEnd = line - 1;
keywordNode.colEnd = endOfPreviousLineColumn;
keywordNode.scope = 2;
if (sourceNode) {
sourceNode.to = parent.to = keywordNode.to;
sourceNode.lineEnd = parent.lineEnd = line - 1;
sourceNode.colEnd = parent.colEnd = endOfPreviousLineColumn;
sourceNode.parent = parent;
if (sourceNode.value.length && !keywordNode.children.includes(sourceNode)) {
keywordNode.children.push(sourceNode);
}
sourceNode = null;
}
keywordNode = parent || root;
parent = keywordNode.parent || root;
scopeDepthCount = 0;
operation = 0;
} else {
operation = operationsByControlIdentifier[controlIdentifierType];
if (sourceNode) {
sourceNode.colEnd = column;
sourceNode.lineEnd = line;
sourceNode.to = position;
sourceNode = null;
}
variableMapOpenerStart = variableMapArgumentStart = inlineStart = 0;
keywordNode = Object.create(astNodeBase);
keywordNode.children = [];
keywordNode.from = position;
keywordNode.colStart = column;
keywordNode.lineStart = line;
keywordNode.keyword = keywordTypes[controlIdentifierType];
if (keywordNode.keyword === 3) {
keywordNode.parent = parent = root;
} else {
keywordNode.parent = parent;
}
if (operation === 4) {
inlineStart = position + 2;
sourceNode = Object.create(astNodeBase);
sourceNode.lineStart = line;
sourceNode.from = inlineStart;
sourceNode.parent = keywordNode;
sourceNode.colStart = column;
}
}
position += skipLength;
} else if (operation === 2 && cursor === 7) {
variableMapOpenerStart = position;
variableMapArgumentStart = position + 1;
operation = 3;
lastNode = keywordNode;
} else if (operation === 3 && cursor === 9) {
lastNode = keywordNode;
variableMapArgumentStart = position + 1;
(keywordNode.variableMapping || (keywordNode.variableMapping = [])).push(code.substring(variableMapArgumentStart, position).trim());
} else if (operation === 3 && cursor === 8) {
lastNode = keywordNode;
if (position - 1 !== variableMapArgumentStart) {
(keywordNode.variableMapping || (keywordNode.variableMapping = [])).push(code.substring(variableMapArgumentStart, position - 1).trim());
}
operation = 2;
variableMapOpenerStart = variableMapArgumentStart = inlineStart = 0;
} else if (operation === 2 && cursor === 10 && keywordNode.keyword === 3) {
lastNode = keywordNode;
inlineStart = position;
operation = 4;
inlineDepthCount = 0;
keywordNode.scope = 2;
} else if (operation === 2 && cursor === 13 && (keywordNode.keyword === 2 || keywordNode.keyword === 1) && keywordNode.scope === 0) {
operation = 0;
lastNode = keywordNode;
keywordNode.scope = 2;
parent.children.push(keywordNode);
parent = keywordNode;
sourceNode = null;
} else if (operation === 2 && cursor === 10 && keywordNode.keyword !== 3) {
lastNode = keywordNode;
inlineStart = position;
operation = 4;
inlineDepthCount = 0;
keywordNode.scope = 1;
sourceNode = Object.create(astNodeBase);
sourceNode.from = position + 1;
sourceNode.parent = keywordNode;
sourceNode.colStart = column;
sourceNode.lineStart = line;
} else if (operation === 4 && cursor === 10) {
lastNode = keywordNode;
inlineDepthCount++;
} else if (operation === 4 && cursor === 11 && inlineDepthCount > 0) {
lastNode = keywordNode;
inlineDepthCount--;
} else if (operation === 4 && cursor === 11 && inlineDepthCount === 0 && keywordNode.keyword !== 3) {
lastNode = keywordNode;
keywordNode.lineEnd = keywordNode.lineStart = line;
keywordNode.to = position;
keywordNode.colEnd = column;
keywordNode.parent = parent;
keywordNode.scope = 1;
if (sourceNode) {
sourceNode.to = position;
sourceNode.parent = keywordNode;
keywordNode.children = [sourceNode];
sourceNode = null;
}
(parent.children || (parent.children = [])).push(keywordNode);
keywordNode = parent;
operation = 0;
sourceNode = Object.create(astNodeBase);
sourceNode.from = position + 1;
sourceNode.parent = keywordNode;
keywordNode.children.push(sourceNode);
} else if (operation === 4 && cursor === 11 && inlineDepthCount === 0 && keywordNode.keyword === 3) {
lastNode = keywordNode;
keywordNode.value = code.substring(inlineStart + 1, position);
keywordNode.lineStart = line;
operation = 0;
root.children.push(keywordNode);
keywordNode.parent = root;
inlineEnd = position + 1;
parent = keywordNode;
} else if (cursor === 13 && parent.keyword === 3 && line - 1 === parent.lineStart) {
parent.functionDeclarationSuffix = code.substring(inlineEnd, position);
if (parent.functionDeclarationSuffix.length && parent.functionDeclarationSuffix.lastIndexOf("{") === parent.functionDeclarationSuffix.length - 1) {
throw new AtbuildParseError(2, `Unnecessary { at ${line - 1}:${endOfPreviousLineColumn} in ${filename}`, `@export function should not have "{" or "}" at the start or end, it will be added at build-time. Use @end at the end.`);
}
} else if (operation === 0 && cursor === 2) {
replacerStart = position;
operation = 5;
lastNode = keywordNode;
} else if (operation === 5 && cursor !== 3 && position - replacerStart > 0 && sourceNode) {
replacerNode = Object.create(astNodeBase);
replacerNode.value = code.substring(replacerStart, position);
replacerNode.from = replacerStart;
replacerNode.to = position - 1;
replacerNode.parent = sourceNode;
(sourceNode.children || (sourceNode.children = [])).push(replacerNode);
operation = 0;
lastNode = sourceNode;
} else if (operation === 7 && cursor !== 3 && cursor !== 1 && cursor !== 2) {
throw new AtbuildParseError(1, `Invalid @export function`, `"@export function" must have a name that starts with "$" on the same line (${line}:${column} in ${filename})`);
} else if (operation === 7 && cursor === 2) {
nameStart = position;
operation = 8;
lastNode = keywordNode;
} else if (operation === 8 && (cursor === 1 || cursor === 13)) {
operation = 8;
keywordNode.name = code.substring(nameStart, position);
operation = 2;
lastNode = keywordNode;
} else if (operation === 8 && cursor === 10) {
operation = 8;
keywordNode.name = code.substring(nameStart, position);
operation = 4;
inlineStart = position;
lastNode = keywordNode;
} else if (operation === 0 && cursor !== 1 && cursor !== 13 && !isLineEmpty && !sourceNode && !(keywordNode && keywordNode.keyword === 3 && line === keywordNode.lineStart)) {
lastNode = sourceNode = Object.create(astNodeBase);
sourceNode.children = [];
sourceNode.keyword = 0;
sourceNode.from = position;
sourceNode.lineStart = line;
sourceNode.colStart = column;
sourceNode.parent = parent;
parent.children.push(sourceNode);
}
}
if (sourceNode && sourceNode.parent === root) {
sourceNode.to = root.to = position;
}
return root;
}
function transformAST(root, code) {
let source = `var _this = {["${PARTIAL_SOURCE_CODE_VARIABLE}"]: ""};
`;
let needsRootSource = false;
for (let i = 0; i < root.children.length; i++) {
if (!needsRootSource && root.children[i].keyword !== 3) {
needsRootSource = true;
}
source += visit(root.children[i], i, root, true, code);
}
if (needsRootSource) {
if (!(source[source.length - 1] === ";" || source[source.length - 2] === ";" && source[source.length - 1] === "\n")) {
source += ";";
}
source += `
module.exports.default = ${SOURCE_CODE_VARIABLE};
${SOURCE_CODE_VARIABLE} = "";
`;
}
return source;
}
const PARTIAL_SOURCE_CODE_VARIABLE = "___source___";
const SOURCE_CODE_VARIABLE = `_this.${PARTIAL_SOURCE_CODE_VARIABLE}`;
const REPLACERS_VARIABLE = "___replacers___";
function visit(node, i, parent, trailingNewline = true, input) {
let functionName = `${ASTNodeKeyword[node.keyword]}___${node.lineStart}_${node.colStart}__${node.lineEnd}_${node.colEnd}`;
let source = "";
switch (node.keyword) {
case 7: {
switch (parent.keyword) {
case 2:
case 3:
case 6: {
node.keyword = 1;
return visit(node, i, parent, trailingNewline, input);
}
case 1: {
node.keyword = 2;
return visit(node, i, parent, trailingNewline, input);
}
default:
throw "Invalid input";
}
}
case 2: {
if (node.scope === 1) {
if (node.parent && node.parent.keyword === 1) {
source += `${SOURCE_CODE_VARIABLE} += (`;
}
if (node.children) {
for (let child of node.children) {
source += visit(child, i + 1, node, false, input);
}
}
} else if (node.scope === 2 && parent.keyword !== 6) {
if (node.children) {
for (let child of node.children) {
source += visit(child, i + 1, node, trailingNewline, input);
}
}
} else if (node.scope === 2 && parent.keyword === 6) {
if (node.children) {
for (let child of node.children) {
source += visit(child, i + 1, node, trailingNewline, input);
}
}
} else {
throw "Not implemented";
}
if (node.scope === 1 && parent && parent.keyword === 1) {
source += `);`;
}
break;
}
case 3: {
source += `
;var ${node.name} = (module.exports.${node.name} = (function ${node.name}(${node.value.trim().split(",").join(", ")})${node.functionDeclarationSuffix} {
let originalThis = _this;
if (!this || typeof this["${PARTIAL_SOURCE_CODE_VARIABLE}"] === 'undefined') {
_this = {
["${PARTIAL_SOURCE_CODE_VARIABLE}"]: ""
}
} else {
_this = this;
}
const buildEval = (function ${functionName}() {
`;
if (node.children) {
for (let child of node.children) {
source += visit(child, i + 1, node, trailingNewline, input);
}
}
source += `
})();
let output = ${SOURCE_CODE_VARIABLE};
_this = originalThis;
return typeof buildEval === 'undefined' ? output : buildEval;
}));
`;
break;
}
case 4: {
throw "Not implemented yet";
break;
}
case 1: {
if (node.children) {
for (let child of node.children) {
source += visit(child, i + 1, node, trailingNewline, input);
}
}
break;
}
case 0: {
let value = input.substring(node.from, Math.min(node.to, input.length));
if (parent.keyword === 2 || parent.keyword === 3 || parent.keyword === 4) {
return trailingNewline ? value : value.trimEnd();
} else if (parent.keyword === 1 || parent.keyword === 6) {
if (node.children && node.children.length && parent && parent.variableMapping && parent.variableMapping.length) {
const slottedValue = [value];
let replacerIndex = -1;
let slotOffset = 0;
let positionOffset = node.from;
let position = 0;
for (let i2 = 0; i2 < node.children.length; i2++) {
const replacer = node.children[i2];
replacerIndex = parent.variableMapping.indexOf(replacer.name);
if (replacerIndex === -1) {
continue;
}
slottedValue.length += 2;
slottedValue[slotOffset++] = value.substring(position - positionOffset, replacer.from - positionOffset);
slottedValue[slotOffset++] = `" + ${REPLACERS_VARIABLE}[${replacerIndex}] + "`;
slottedValue[slotOffset++] = value.substring(replacer.to - positionOffset + 1);
}
value = slottedValue.join("");
}
source += `${SOURCE_CODE_VARIABLE} += "${value.replace(/\n/gm, "\\n").replace(/"/gm, '\\"')}";${trailingNewline ? "\n" : ""}`;
} else {
throw "Unhandled keyword type";
}
break;
}
default: {
debugger;
throw `Invalid ASTNodeKeyword: ${node.keyword}`;
break;
}
}
return source;
}
export default require_atbuild();
//# sourceMappingURL=atbuild.js.map
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc