v0.0.11: Added unit-tests, solved the key={...} problem, updated the build/watch configuration of CSX to be able to build minified and non-minified bundle outputs, as well as a CJS version of lib/ (for consuming in Node-environment, like Jest). The previous tests were renamed to examples, and should still need to be updated.

This commit is contained in:
2020-04-14 11:54:30 +02:00
parent 05f0e66a42
commit b95e5506d2
121 changed files with 3406 additions and 4929 deletions

View File

@@ -1,3 +0,0 @@
src
test
*.log

View File

@@ -1,19 +0,0 @@
# @babel/helpers
> Collection of helper functions used by Babel transforms.
See our website [@babel/helpers](https://babeljs.io/docs/en/next/babel-helpers.html) for more information.
## Install
Using npm:
```sh
npm install --save-dev @babel/helpers
```
or using yarn:
```sh
yarn add @babel/helpers --dev
```

File diff suppressed because it is too large Load Diff

View File

@@ -1,276 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.get = get;
exports.minVersion = minVersion;
exports.getDependencies = getDependencies;
exports.ensure = ensure;
exports.default = exports.list = void 0;
var _traverse = _interopRequireDefault(require("@babel/traverse"));
var t = _interopRequireWildcard(require("@babel/types"));
var _helpers = _interopRequireDefault(require("./helpers"));
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; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; if (obj != null) { 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 makePath(path) {
const parts = [];
for (; path.parentPath; path = path.parentPath) {
parts.push(path.key);
if (path.inList) parts.push(path.listKey);
}
return parts.reverse().join(".");
}
function getHelperMetadata(file) {
const globals = new Set();
const localBindingNames = new Set();
const dependencies = new Map();
let exportName;
let exportPath;
const exportBindingAssignments = [];
const importPaths = [];
const importBindingsReferences = [];
(0, _traverse.default)(file, {
ImportDeclaration(child) {
const name = child.node.source.value;
if (!_helpers.default[name]) {
throw child.buildCodeFrameError(`Unknown helper ${name}`);
}
if (child.get("specifiers").length !== 1 || !child.get("specifiers.0").isImportDefaultSpecifier()) {
throw child.buildCodeFrameError("Helpers can only import a default value");
}
const bindingIdentifier = child.node.specifiers[0].local;
dependencies.set(bindingIdentifier, name);
importPaths.push(makePath(child));
},
ExportDefaultDeclaration(child) {
const decl = child.get("declaration");
if (decl.isFunctionDeclaration()) {
if (!decl.node.id) {
throw decl.buildCodeFrameError("Helpers should give names to their exported func declaration");
}
exportName = decl.node.id.name;
}
exportPath = makePath(child);
},
ExportAllDeclaration(child) {
throw child.buildCodeFrameError("Helpers can only export default");
},
ExportNamedDeclaration(child) {
throw child.buildCodeFrameError("Helpers can only export default");
},
Statement(child) {
if (child.isModuleDeclaration()) return;
child.skip();
}
});
(0, _traverse.default)(file, {
Program(path) {
const bindings = path.scope.getAllBindings();
Object.keys(bindings).forEach(name => {
if (name === exportName) return;
if (dependencies.has(bindings[name].identifier)) return;
localBindingNames.add(name);
});
},
ReferencedIdentifier(child) {
const name = child.node.name;
const binding = child.scope.getBinding(name, true);
if (!binding) {
globals.add(name);
} else if (dependencies.has(binding.identifier)) {
importBindingsReferences.push(makePath(child));
}
},
AssignmentExpression(child) {
const left = child.get("left");
if (!(exportName in left.getBindingIdentifiers())) return;
if (!left.isIdentifier()) {
throw left.buildCodeFrameError("Only simple assignments to exports are allowed in helpers");
}
const binding = child.scope.getBinding(exportName);
if (binding && binding.scope.path.isProgram()) {
exportBindingAssignments.push(makePath(child));
}
}
});
if (!exportPath) throw new Error("Helpers must default-export something.");
exportBindingAssignments.reverse();
return {
globals: Array.from(globals),
localBindingNames: Array.from(localBindingNames),
dependencies,
exportBindingAssignments,
exportPath,
exportName,
importBindingsReferences,
importPaths
};
}
function permuteHelperAST(file, metadata, id, localBindings, getDependency) {
if (localBindings && !id) {
throw new Error("Unexpected local bindings for module-based helpers.");
}
if (!id) return;
const {
localBindingNames,
dependencies,
exportBindingAssignments,
exportPath,
exportName,
importBindingsReferences,
importPaths
} = metadata;
const dependenciesRefs = {};
dependencies.forEach((name, id) => {
dependenciesRefs[id.name] = typeof getDependency === "function" && getDependency(name) || id;
});
const toRename = {};
const bindings = new Set(localBindings || []);
localBindingNames.forEach(name => {
let newName = name;
while (bindings.has(newName)) newName = "_" + newName;
if (newName !== name) toRename[name] = newName;
});
if (id.type === "Identifier" && exportName !== id.name) {
toRename[exportName] = id.name;
}
(0, _traverse.default)(file, {
Program(path) {
const exp = path.get(exportPath);
const imps = importPaths.map(p => path.get(p));
const impsBindingRefs = importBindingsReferences.map(p => path.get(p));
const decl = exp.get("declaration");
if (id.type === "Identifier") {
if (decl.isFunctionDeclaration()) {
exp.replaceWith(decl);
} else {
exp.replaceWith(t.variableDeclaration("var", [t.variableDeclarator(id, decl.node)]));
}
} else if (id.type === "MemberExpression") {
if (decl.isFunctionDeclaration()) {
exportBindingAssignments.forEach(assignPath => {
const assign = path.get(assignPath);
assign.replaceWith(t.assignmentExpression("=", id, assign.node));
});
exp.replaceWith(decl);
path.pushContainer("body", t.expressionStatement(t.assignmentExpression("=", id, t.identifier(exportName))));
} else {
exp.replaceWith(t.expressionStatement(t.assignmentExpression("=", id, decl.node)));
}
} else {
throw new Error("Unexpected helper format.");
}
Object.keys(toRename).forEach(name => {
path.scope.rename(name, toRename[name]);
});
for (const path of imps) path.remove();
for (const path of impsBindingRefs) {
const node = t.cloneNode(dependenciesRefs[path.node.name]);
path.replaceWith(node);
}
path.stop();
}
});
}
const helperData = Object.create(null);
function loadHelper(name) {
if (!helperData[name]) {
const helper = _helpers.default[name];
if (!helper) {
throw Object.assign(new ReferenceError(`Unknown helper ${name}`), {
code: "BABEL_HELPER_UNKNOWN",
helper: name
});
}
const fn = () => {
return t.file(helper.ast());
};
const metadata = getHelperMetadata(fn());
helperData[name] = {
build(getDependency, id, localBindings) {
const file = fn();
permuteHelperAST(file, metadata, id, localBindings, getDependency);
return {
nodes: file.program.body,
globals: metadata.globals
};
},
minVersion() {
return helper.minVersion;
},
dependencies: metadata.dependencies
};
}
return helperData[name];
}
function get(name, getDependency, id, localBindings) {
return loadHelper(name).build(getDependency, id, localBindings);
}
function minVersion(name) {
return loadHelper(name).minVersion();
}
function getDependencies(name) {
return Array.from(loadHelper(name).dependencies.values());
}
function ensure(name) {
loadHelper(name);
}
const list = Object.keys(_helpers.default).map(name => name.replace(/^_/, "")).filter(name => name !== "__esModule");
exports.list = list;
var _default = get;
exports.default = _default;

View File

@@ -1,21 +0,0 @@
{
"name": "@babel/helpers",
"version": "7.7.0",
"description": "Collection of helper functions used by Babel transforms.",
"author": "Sebastian McKenzie <sebmck@gmail.com>",
"homepage": "https://babeljs.io/",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"repository": "https://github.com/babel/babel/tree/master/packages/babel-helpers",
"main": "lib/index.js",
"dependencies": {
"@babel/template": "^7.7.0",
"@babel/traverse": "^7.7.0",
"@babel/types": "^7.7.0"
},
"devDependencies": {
"@babel/helper-plugin-test-runner": "^7.0.0"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,291 +0,0 @@
import traverse from "@babel/traverse";
import * as t from "@babel/types";
import helpers from "./helpers";
function makePath(path) {
const parts = [];
for (; path.parentPath; path = path.parentPath) {
parts.push(path.key);
if (path.inList) parts.push(path.listKey);
}
return parts.reverse().join(".");
}
/**
* Given a file AST for a given helper, get a bunch of metadata about it so that Babel can quickly render
* the helper is whatever context it is needed in.
*/
function getHelperMetadata(file) {
const globals = new Set();
const localBindingNames = new Set();
// Maps imported identifier -> helper name
const dependencies = new Map();
let exportName;
let exportPath;
const exportBindingAssignments = [];
const importPaths = [];
const importBindingsReferences = [];
traverse(file, {
ImportDeclaration(child) {
const name = child.node.source.value;
if (!helpers[name]) {
throw child.buildCodeFrameError(`Unknown helper ${name}`);
}
if (
child.get("specifiers").length !== 1 ||
!child.get("specifiers.0").isImportDefaultSpecifier()
) {
throw child.buildCodeFrameError(
"Helpers can only import a default value",
);
}
const bindingIdentifier = child.node.specifiers[0].local;
dependencies.set(bindingIdentifier, name);
importPaths.push(makePath(child));
},
ExportDefaultDeclaration(child) {
const decl = child.get("declaration");
if (decl.isFunctionDeclaration()) {
if (!decl.node.id) {
throw decl.buildCodeFrameError(
"Helpers should give names to their exported func declaration",
);
}
exportName = decl.node.id.name;
}
exportPath = makePath(child);
},
ExportAllDeclaration(child) {
throw child.buildCodeFrameError("Helpers can only export default");
},
ExportNamedDeclaration(child) {
throw child.buildCodeFrameError("Helpers can only export default");
},
Statement(child) {
if (child.isModuleDeclaration()) return;
child.skip();
},
});
traverse(file, {
Program(path) {
const bindings = path.scope.getAllBindings();
Object.keys(bindings).forEach(name => {
if (name === exportName) return;
if (dependencies.has(bindings[name].identifier)) return;
localBindingNames.add(name);
});
},
ReferencedIdentifier(child) {
const name = child.node.name;
const binding = child.scope.getBinding(name, /* noGlobal */ true);
if (!binding) {
globals.add(name);
} else if (dependencies.has(binding.identifier)) {
importBindingsReferences.push(makePath(child));
}
},
AssignmentExpression(child) {
const left = child.get("left");
if (!(exportName in left.getBindingIdentifiers())) return;
if (!left.isIdentifier()) {
throw left.buildCodeFrameError(
"Only simple assignments to exports are allowed in helpers",
);
}
const binding = child.scope.getBinding(exportName);
if (binding && binding.scope.path.isProgram()) {
exportBindingAssignments.push(makePath(child));
}
},
});
if (!exportPath) throw new Error("Helpers must default-export something.");
// Process these in reverse so that mutating the references does not invalidate any later paths in
// the list.
exportBindingAssignments.reverse();
return {
globals: Array.from(globals),
localBindingNames: Array.from(localBindingNames),
dependencies,
exportBindingAssignments,
exportPath,
exportName,
importBindingsReferences,
importPaths,
};
}
/**
* Given a helper AST and information about how it will be used, update the AST to match the usage.
*/
function permuteHelperAST(file, metadata, id, localBindings, getDependency) {
if (localBindings && !id) {
throw new Error("Unexpected local bindings for module-based helpers.");
}
if (!id) return;
const {
localBindingNames,
dependencies,
exportBindingAssignments,
exportPath,
exportName,
importBindingsReferences,
importPaths,
} = metadata;
const dependenciesRefs = {};
dependencies.forEach((name, id) => {
dependenciesRefs[id.name] =
(typeof getDependency === "function" && getDependency(name)) || id;
});
const toRename = {};
const bindings = new Set(localBindings || []);
localBindingNames.forEach(name => {
let newName = name;
while (bindings.has(newName)) newName = "_" + newName;
if (newName !== name) toRename[name] = newName;
});
if (id.type === "Identifier" && exportName !== id.name) {
toRename[exportName] = id.name;
}
traverse(file, {
Program(path) {
// We need to compute these in advance because removing nodes would
// invalidate the paths.
const exp = path.get(exportPath);
const imps = importPaths.map(p => path.get(p));
const impsBindingRefs = importBindingsReferences.map(p => path.get(p));
const decl = exp.get("declaration");
if (id.type === "Identifier") {
if (decl.isFunctionDeclaration()) {
exp.replaceWith(decl);
} else {
exp.replaceWith(
t.variableDeclaration("var", [t.variableDeclarator(id, decl.node)]),
);
}
} else if (id.type === "MemberExpression") {
if (decl.isFunctionDeclaration()) {
exportBindingAssignments.forEach(assignPath => {
const assign = path.get(assignPath);
assign.replaceWith(t.assignmentExpression("=", id, assign.node));
});
exp.replaceWith(decl);
path.pushContainer(
"body",
t.expressionStatement(
t.assignmentExpression("=", id, t.identifier(exportName)),
),
);
} else {
exp.replaceWith(
t.expressionStatement(t.assignmentExpression("=", id, decl.node)),
);
}
} else {
throw new Error("Unexpected helper format.");
}
Object.keys(toRename).forEach(name => {
path.scope.rename(name, toRename[name]);
});
for (const path of imps) path.remove();
for (const path of impsBindingRefs) {
const node = t.cloneNode(dependenciesRefs[path.node.name]);
path.replaceWith(node);
}
// We only use "traverse" for all the handy scoping helpers, so we can stop immediately without
// actually doing the traversal.
path.stop();
},
});
}
const helperData = Object.create(null);
function loadHelper(name) {
if (!helperData[name]) {
const helper = helpers[name];
if (!helper) {
throw Object.assign(new ReferenceError(`Unknown helper ${name}`), {
code: "BABEL_HELPER_UNKNOWN",
helper: name,
});
}
const fn = () => {
return t.file(helper.ast());
};
const metadata = getHelperMetadata(fn());
helperData[name] = {
build(getDependency, id, localBindings) {
const file = fn();
permuteHelperAST(file, metadata, id, localBindings, getDependency);
return {
nodes: file.program.body,
globals: metadata.globals,
};
},
minVersion() {
return helper.minVersion;
},
dependencies: metadata.dependencies,
};
}
return helperData[name];
}
export function get(
name,
getDependency?: string => ?t.Expression,
id?,
localBindings?: string[],
) {
return loadHelper(name).build(getDependency, id, localBindings);
}
export function minVersion(name: string) {
return loadHelper(name).minVersion();
}
export function getDependencies(name: string): $ReadOnlyArray<string> {
return Array.from(loadHelper(name).dependencies.values());
}
export function ensure(name: string) {
loadHelper(name);
}
export const list = Object.keys(helpers)
.map(name => name.replace(/^_/, ""))
.filter(name => name !== "__esModule");
export default get;

View File

@@ -1 +0,0 @@
REPLACE_ME;

View File

@@ -1,3 +0,0 @@
{
"plugins": ["./plugin"]
}

View File

@@ -1,5 +0,0 @@
function _$_basic_main() { return _$_basic_dependency(); }
function _$_basic_dependency() {}
_$_basic_main;

View File

@@ -1,25 +0,0 @@
const defineHelper = require("../../../helpers/define-helper").default;
const dependency = defineHelper(__dirname, "dependency", `
export default function fn() {}
`);
const main = defineHelper(__dirname, "main", `
import dep from "${dependency}";
export default function helper() {
return dep();
}
`);
module.exports = function() {
return {
visitor: {
Identifier(path) {
if (path.node.name !== "REPLACE_ME") return;
const helper = this.addHelper(main);
path.replaceWith(helper);
},
},
};
};

View File

@@ -1 +0,0 @@
REPLACE_ME;

View File

@@ -1,3 +0,0 @@
{
"plugins": ["./plugin"]
}

View File

@@ -1,7 +0,0 @@
function _$_deep_main() { return _$_deep_dependency(); }
function _$_deep_dependency() { return _$_deep_dependencyDeep; }
function _$_deep_dependencyDeep() {}
_$_deep_main;

View File

@@ -1,30 +0,0 @@
const defineHelper = require("../../../helpers/define-helper").default;
const dependencyDeep = defineHelper(__dirname, "dependencyDeep", `
export default function fn() {}
`)
const dependency = defineHelper(__dirname, "dependency", `
import f from "${dependencyDeep}";
export default function fn() { return f; }
`);
const main = defineHelper(__dirname, "main", `
import dep from "${dependency}";
export default function helper() {
return dep();
}
`);
module.exports = function() {
return {
visitor: {
Identifier(path) {
if (path.node.name !== "REPLACE_ME") return;
const helper = this.addHelper(main);
path.replaceWith(helper);
},
},
};
};

View File

@@ -1 +0,0 @@
REPLACE_ME;

View File

@@ -1,4 +0,0 @@
{
"plugins": ["./plugin"],
"throws": " "
}

View File

@@ -1,21 +0,0 @@
const defineHelper = require("../../../helpers/define-helper").default;
const main = defineHelper(__dirname, "main", `
import dep from "(!!!)%-..a,4892 missing";
export default function helper() {
return dep();
}
`);
module.exports = function() {
return {
visitor: {
Identifier(path) {
if (path.node.name !== "REPLACE_ME") return;
const helper = this.addHelper(main);
path.replaceWith(helper);
},
},
};
};

View File

@@ -1 +0,0 @@
REPLACE_ME;

View File

@@ -1,3 +0,0 @@
{
"plugins": ["./plugin"]
}

View File

@@ -1,7 +0,0 @@
function _$_multiple_main() { return _$_multiple_dependency() + _$_multiple_dependency2(); }
function _$_multiple_dependency2() { 1; }
function _$_multiple_dependency() { 0; }
_$_multiple_main;

View File

@@ -1,30 +0,0 @@
const defineHelper = require("../../../helpers/define-helper").default;
const dependency1 = defineHelper(__dirname, "dependency1", `
export default function fn() { 0; }
`);
const dependency2 = defineHelper(__dirname, "dependency2", `
export default function fn() { 1; }
`);
const main = defineHelper(__dirname, "main", `
import dep1 from "${dependency1}";
import dep2 from "${dependency2}";
export default function helper() {
return dep1() + dep2();
}
`);
module.exports = function() {
return {
visitor: {
Identifier(path) {
if (path.node.name !== "REPLACE_ME") return;
const helper = this.addHelper(main);
path.replaceWith(helper);
},
},
};
};

View File

@@ -1,3 +0,0 @@
{
"plugins": ["./plugin"]
}

View File

@@ -1,9 +0,0 @@
let _foo = "main";
function _$_renameBindingEqual_main() { return _$_renameBindingEqual_dependency() + _foo; }
let foo = "dependency";
function _$_renameBindingEqual_dependency() { return foo; }
_$_renameBindingEqual_main;

View File

@@ -1,28 +0,0 @@
const defineHelper = require("../../../helpers/define-helper").default;
const dependency = defineHelper(__dirname, "dependency", `
let foo = "dependency";
export default function fn() { return foo }
`);
const main = defineHelper(__dirname, "main", `
import dep from "${dependency}";
let foo = "main";
export default function helper() {
return dep() + foo;
}
`);
module.exports = function() {
return {
visitor: {
Identifier(path) {
if (path.node.name !== "REPLACE_ME") return;
const helper = this.addHelper(main);
path.replaceWith(helper);
},
},
};
};

View File

@@ -1,3 +0,0 @@
REPLACE_ME;
let Promise = "I will be a good guy!";

View File

@@ -1,3 +0,0 @@
{
"plugins": ["./plugin"]
}

View File

@@ -1,6 +0,0 @@
function _$_renameDeepGlobal_main() { return _$_renameDeepGlobal_dependency() || Promise; }
function _$_renameDeepGlobal_dependency() { return Promise; }
_$_renameDeepGlobal_main;
let _Promise = "I will be a good guy!";

View File

@@ -1,27 +0,0 @@
const defineHelper = require("../../../helpers/define-helper").default;
const dependency = defineHelper(__dirname, "dependency", `
export default function fn() {
return Promise;
}
`);
const main = defineHelper(__dirname, "main", `
import dep from "${dependency}";
export default function helper() {
return dep() || Promise;
}
`);
module.exports = function() {
return {
visitor: {
Identifier(path) {
if (path.node.name !== "REPLACE_ME") return;
const helper = this.addHelper(main);
path.replaceWith(helper);
},
},
};
};

View File

@@ -1,2 +0,0 @@
REPLACE_ME_1;
REPLACE_ME_2;

View File

@@ -1,3 +0,0 @@
{
"plugins": ["./plugin"]
}

View File

@@ -1,6 +0,0 @@
function _$_reuseDependency_main() { return _$_reuseDependency_dependency(); }
function _$_reuseDependency_dependency() { 0; }
_$_reuseDependency_main;
_$_reuseDependency_dependency;

View File

@@ -1,29 +0,0 @@
const defineHelper = require("../../../helpers/define-helper").default;
const dependency = defineHelper(__dirname, "dependency", `
export default function fn() { 0; }
`);
const main = defineHelper(__dirname, "main", `
import dep from "${dependency}";
export default function helper() {
return dep();
}
`);
module.exports = function() {
return {
visitor: {
Identifier(path) {
if (path.node.name === "REPLACE_ME_1") {
const mainHelper = this.addHelper(main);
path.replaceWith(mainHelper);
} else if (path.node.name === "REPLACE_ME_2") {
const dependencyHelper = this.addHelper(dependency);
path.replaceWith(dependencyHelper);
}
},
},
};
};

View File

@@ -1,3 +0,0 @@
{
"plugins": ["./plugin"]
}

View File

@@ -1,5 +0,0 @@
function _$_variableSameNameDependency_main() { let x = _$_variableSameNameDependency_dependency; return function (dep) { return x() + dep; }; }
function _$_variableSameNameDependency_dependency() {}
_$_variableSameNameDependency_main;

View File

@@ -1,28 +0,0 @@
const defineHelper = require("../../../helpers/define-helper").default;
const dependency = defineHelper(__dirname, "dependency", `
export default function fn() {}
`);
const main = defineHelper(__dirname, "main", `
import dep from "${dependency}";
export default function helper() {
let x = dep;
return function (dep) {
return x() + dep;
}
}
`);
module.exports = function() {
return {
visitor: {
Identifier(path) {
if (path.node.name !== "REPLACE_ME") return;
const helper = this.addHelper(main);
path.replaceWith(helper);
},
},
};
};

View File

@@ -1,26 +0,0 @@
import path from "path";
import template from "@babel/template";
import helpers from "../../lib/helpers";
function getHelperId(dir, name) {
const testName = path.basename(dir);
return `_$_${testName}_${name}`;
}
export default function defineHelper(
dir: string,
name: string,
code: string,
): string {
const id = getHelperId(dir, name);
if (id in helpers) {
throw new Error(`The ${id} helper is already defined.`);
}
Object.defineProperty(helpers, id, {
value: {
minVersion: "7.0.0-beta.0",
ast: template.program(code),
},
});
return id;
}

View File

@@ -1,3 +0,0 @@
import runner from "@babel/helper-plugin-test-runner";
runner(__dirname);

19
packages/csx/cfg/.babelrc Normal file
View File

@@ -0,0 +1,19 @@
{
"sourceMaps": "both",
"presets": [
["@babel/preset-env", {
"targets": {
"node": "current"
}
}]
],
"plugins": [
[ "@babel/plugin-proposal-decorators", { "legacy": true }],
[ "@babel/plugin-proposal-class-properties", { "loose": true } ],
[ "@babel/plugin-proposal-private-methods", {"loose": true } ],
[ "@babel/plugin-proposal-optional-chaining" ],
[ "@babel/plugin-proposal-nullish-coalescing-operator" ],
[ "@babel/plugin-proposal-export-namespace-from" ],
[ "@babel/plugin-proposal-export-default-from" ]
]
}

54
packages/csx/cfg/build.js Normal file
View File

@@ -0,0 +1,54 @@
// Build configuration
import { CsxConfig, runArgs } from "./config";
import { logBundle } from './log-bundle';
// Rollup and plugins
import * as rollup from 'rollup';
/**
* Build CSX
* @param {CsxConfig} cfg - Configuration on how to build
* @param {boolean} clean - Clean the dist and lib-dirs first
* @returns {Promise<CsxConfig>}
*/
async function build(cfg, clean){
let {src, dist, lib, root, sourceGlob} = cfg;
root = root.from('');// This is a bug in @cerxes/host!
// Clean host
if(clean!==false){
await Promise.all([
dist.remove('', {recursive: true}),
lib.remove('', {recursive: true}),
]);
console.log("Output-dirs cleaned!");
}
let appStart = new Date();
let sources = await src.glob(sourceGlob);
let targets = cfg.configureTargets(sources);
let builds = targets.map(async target=>{
return await Promise.all(
target.sources.map(async source=>{
let bundle = await rollup.rollup(source.config);
let written = await bundle.write(source.config.output);
let stats = await root.stat(source.config.output.file);
logBundle(root.relative(source.config.output.file), (new Date()).getTime()-appStart.getTime(), stats);
return {bundle, written};
})
);
})
let results = await Promise.all(builds);
console.log("CSX built");
return results
}
// Execute self if script is run as entry (e.g. "node build.js")
if(require.main === module) {
// Run !
build(new CsxConfig());
}

170
packages/csx/cfg/config.js Normal file
View File

@@ -0,0 +1,170 @@
// @cerxes
import { host, Host } from "@cerxes/host";
// Rollup and plugins
import babel from 'rollup-plugin-babel';
import resolve from 'rollup-plugin-node-resolve';
import { terser } from 'rollup-plugin-terser';
// Proc args (this is weird to be here, but it is the same for build/watch, and should be kept in sync as much as possible
import process from "process";
import * as rollup from "rollup";
import { logBundle } from "./log-bundle";
let args = process.argv.slice(2);
const runArgSeperator = args.indexOf('--');
export let buildTargets = ['lib-es', 'lib-cjs', 'dist-es', 'dist-es-min', 'dist-cjs', 'dist-cjs-min'];
if (runArgSeperator >= 0) {
buildTargets = args.slice(runArgSeperator + 1);
args = args.slice(0, runArgSeperator);
}
export class CsxConfig {
constructor({
src,
dist,
root,
opts
} = {}) {
const { watch } = opts || {};
this.root = root ? host.from(root) : host.from('.');// (This host.from('.') would not've been necessary if @cerxes/host did not have a bug on this one)
this.src = this.root.from(src || 'src');
this.dist = this.root.from(dist || 'dist');
this.lib = this.root.from(dist || 'lib');
}
/** @type {Host} */
src;
/**
* Directory to use for dist output (single bundle)
* @type {Host} */
dist;
/**
* Directory to use for library output (transpiled, file by file)
* @type {Host} */
lib;
/** @type {string} */
sourceGlob = "**/*.@(*.js|js)";
/**
* Get rollup-config to compile a source file
* @param {string} file
* @param {string} out
* @param {object} [opts]
* @param {boolean} [opts.es] - Export in es6 format (or cjs when false)
* @param {boolean} [opts.single] - Compile single file (e.g. don't follow imports)
* @param {boolean} [opts.sourcemap] - Include sourcemaps
* @param {boolean} [opts.minified] - Minify the output (e.g. Terser-plugin)
* @returns {{output: {file: string, sourcemap: boolean, plugins: Array<Object>, format: string}, input: string, plugins: ...*[]}}
*/
srcCfg = (file, out = undefined, opts) => {
let inputFile = this.src.resolve(file);
let outputFile = this.dist.resolve(out || file);
return ({
// Compile the actual app
input: inputFile,
output: {
file: outputFile,
format: opts?.es? 'es' : 'cjs', // NodeJS
sourcemap: opts?.sourcemap ?? true,
},
external: (id, parentId, isResolved) => {
if (!id || id[0] === '\0') return false;// Special cases
else {
let split = (id || "").split('/');
let first = id[0] === '@' ? split.slice(0, 2).join('/') : split[0];
let absId = first === '..' || first === '.' ? this.src.resolve(parentId, id) : id;
if (absId) {
if(absId.indexOf(this.src.workingDirectory) >= 0 && opts?.single) return true; // Own source-file => external when single mode enabled
}
// Any other unresolved identifier(which should be resolved further)
return false; // The light, inline them
}
},
treeshake: !opts?.single,
plugins: [
// babel
babel({
babelrc: true,
}),
// NodeJS-style resolving (Its a shame this is not a Rollup-default, we need this for index.js files to work!)
resolve({
extensions: ['.mjs', '.js', '.jsx', '.json'],
preferBuiltins: true, mainFields: ['browser']
}),
opts?.minified ? terser() : null
].filter(t=>t)
})
};
/**
* @param {string} sources
* @returns {TargetConfig[]}
*/
configureTargets = (sources)=>{
return buildTargets.map(target=>{
let {lib,dist,root} = this;
let [type, format, minified] = target.split("-");
let inputs = type==='lib'? sources : ['index.js'];
let srcOpts = {
es: format==='es',
single: type==='lib',
sourcemap: true,// Just always there for now
minified: !!minified
};
let outDir = type==='lib'? lib : dist;
return new TargetConfig({
target,
sources: inputs.map(input=>{
let outName = input.replace(
/\.js$/,
`.${[
type==='dist'? srcOpts.es?'es':'cjs' : null,
srcOpts.minified?'min':null,
'js'
].filter(x=>x).join('.')}`);
if(type==='lib') outName = [srcOpts.es?'es':'cjs',outName].join('/');
return new SourceConfig({
source: input,
config: this.srcCfg(input, outDir.resolve(outName), srcOpts)
});
})
});
});
}
}
/**
* Configure a source-file for build (single target)
*/
export class SourceConfig{
/** @param {SourceConfig} c */
constructor(c){
this.source = c.source;
this.config = c.config;
}
/** @type {string} */
source;
/** @type {RollupOptions} */
config;
}
/**
* Represent the configuration for a specific build target (e.g. lib-es, dist-cjs, ...)
*/
export class TargetConfig{
/** @param {TargetConfig} c */
constructor(c){
this.target = c.target;
this.sources = c.sources;
}
/** @type {string} */
target;
/** @type {SourceConfig[]} */
sources;
}

View File

@@ -0,0 +1,25 @@
export function logBundle(file, duration, stats){
let paddedFileName = file;
while(paddedFileName.length<40){ paddedFileName += ' ';}
let paddedDuration = (Math.round(duration/10)/100).toFixed(2)+"s";
let durationAnsi = (duration/1000)<10? '' // GREEN is <10sec
: (duration/1000)<60? "" // YELLOW if >10sec
: ""; // RED if >60sec
while(paddedDuration.length<8){ paddedDuration = ' '+paddedDuration;}
let fileSize = stats.size;
let paddedSize = fileSize/1024;
let unit = 'kb';
if(paddedSize>1024){
paddedSize /= 1024;
unit = 'mb';
}
paddedSize = (Math.round(paddedSize*10)/10).toFixed(1)+unit;
while(paddedSize.length<8){ paddedSize = ' ' + paddedSize;}
let sizeAnsi = (fileSize/1024)<50? '' // GREEN is <50kb
: (fileSize/1024)<500? "" // YELLOW if >50kb
: ""; // RED if >500kb
console.log([paddedFileName, `${durationAnsi}${paddedDuration}`, `${sizeAnsi}${paddedSize}`].join('\t'))
}

View File

@@ -0,0 +1,4 @@
// Configure babel-register (as this currently contains no special configuration, this could be removed, but it is here for extendability later on)
require('@babel/register')({
babelrc: true
});

93
packages/csx/cfg/watch.js Normal file
View File

@@ -0,0 +1,93 @@
// TODO this is yet to be updated!
// Observables
import {Observable} from "zen-observable/lib/Observable";
import {merge, combineLatest, zip} from "zen-observable/lib/extras";
// Rollup and plugins
import * as rollup from 'rollup';
// Build configuration
import { CsxConfig, runArgs } from "./config";
import { logBundle } from "./log-bundle";
/**
* Watch the CSX-sourcecode and rebuild as needed
*
* @param {CsxConfig} cfg - Configuration on how to build
* @param {boolean} clean - Clean the dist-dir first
* @returns {Promise<CsxConfig>}
*/
async function watch(cfg, clean) {
let { src, dist, lib, sourceGlob, root } = cfg;
// Clean host
if (clean !== false) {
await Promise.all([
dist.remove('', {recursive: true}),
lib.remove('', {recursive: true}),
]);
console.log("Output-dirs cleaned!");
}
let discovered = false;
let appObservable = src.watch().glob(sourceGlob).map((event)=>{
let updateStart = new Date();
let deletedFiles = event.changes.filter(t=>t.event === 'removed').map(t=>t.file);
let modifiedFiles = event.changes.filter(t=>t.event !== 'removed').map(t=>t.file);
// somehow this runs twice now, TODO FIX PROPERLY!
if(event.type==='discovery') {
if (discovered) return;
else discovered = true;
}
let targets = cfg.configureTargets(event.files);
let builds = targets.map(async target=>{
return {target, sources: await Promise.all(
target.sources.map(async source=>{
let fileStart = new Date();
let bundle = await rollup.rollup(source.config);
let written = await bundle.write(source.config.output);
let stats = await root.stat(source.config.output.file);
logBundle(root.relative(source.config.output.file), (new Date()).getTime()-fileStart.getTime(), stats);
return {bundle, written, source, stats};
})
)};
})
return (async()=>{
let results = await Promise.all(builds);
return {
files: event.files,
duration: ((new Date()).getTime()-updateStart.getTime()),
initialBuild: event.type==='discovery',
results: results
};
})();
});
appObservable.subscribe( async (buildOp)=>{
let {results, files, duration, initialBuild} = await buildOp;
console.log(`CSX ${initialBuild?'build':'updated'} in ${duration/1000}s: ${(new Date()).toUTCString()}`);
});
return appObservable;
}
// Execute self if script is run as entry (e.g. "node watch.js")
if (require.main === module) {
// Run !
watch(new CsxConfig({
opts: { watch: true }
})).then(
(obs) => {
obs.subscribe(() => {
//console.log(`Server updated: ${(new Date()).toUTCString()}`);
})
}
);
}

View File

@@ -1,6 +1,6 @@
{
"name": "@cerxes/csx",
"version": "0.0.10",
"version": "0.0.11",
"author": "Miel Truyen <miel.truyen@cerxes.net>",
"description": "CSX is a minimalistic UI-framework inspired by React+JSX for usage with WebComponents.",
"repository": {
@@ -26,17 +26,13 @@
"rollup-plugin-terser": "5.1.2",
"rollup-plugin-json": "4.0.0",
"acorn-private-class-elements": "0.1.2",
"npm-run-all": "latest"
"@cerxes/host": "latest"
},
"scripts": {
"build": "npm-run-all -p build-cjs build-es6",
"watch": "npm-run-all -p watch-cjs watch-es6",
"build-cjs": "rollup -c",
"watch-cjs": "rollup -c -w",
"build-es6": "npx babel ./src --out-dir=lib",
"watch-es6": "npx babel ./src --out-dir=lib -w",
"build": "node -r ./cfg/register ./cfg/build.js",
"watch": "node -r ./cfg/register ./cfg/watch.js",
"npm-publish": "npm run build && npm publish --registry https://npm.cerxes.net --tag latest"
},
"module": "./lib/index.js",
"main": "./dist/index.js"
"module": "./lib/es/index.js",
"main": "./dist/index.cjs.js"
}

View File

@@ -1,27 +0,0 @@
import babel from 'rollup-plugin-babel';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import json from "rollup-plugin-json";
// `npm run build` -> `production` is true
// `npm run dev` -> `production` is false
const production = !process.env.ROLLUP_WATCH;
export default {
input: 'src/index.js',
output: {
file: 'dist/index.js',
format: 'cjs', // immediately-invoked function expression — suitable for <script> tags
sourcemap: true
},
plugins: [
json(), // Add json support (sadly in rollup we have to do this explicity, despite the NodeJS algorithm supporitng this by defulat
babel(), // babel (the reason we're doing all of this, babel transpiling)
resolve({// node_modules (again we have to add support in rollup for something that is NodeJS default)
}),
production && terser() // minify, but only in production
],
external: [
]
};

View File

@@ -150,13 +150,12 @@ export function render(vnode, opts = {}) {
};
oldVChildren[ childType ].push(oldItem);
if (next.props?.key) {
state.keyedElements.set(next.key, oldItem);
state.keyedElements.set(next.props?.key, oldItem);
}
}
}
}
let sortedChildTypes = Array.from(childTypes).sort((a, b) => a === 'node' ? 1 : -1); // Always do ChildNode-types last
let queuedItems = [];
/**@type {VRenderQueueItem}*/ let previous = null;
@@ -188,9 +187,10 @@ export function render(vnode, opts = {}) {
}
}
if (previous) {
previous.item.host.after(oldChild.element);
previous.host.after(oldChild.element);
} else {
item.parent.prepend(oldChild.element);
//item.parent.prepend(oldChild.element);
item.host.prepend(oldChild.element);
}
}
}

View File

@@ -28,4 +28,4 @@ import "./render-item";// Info about what we're rendering and where to
* @method
* @name VNodeRenderer#remove
* @param {VRenderItem} item
*/
*/

View File

@@ -36,4 +36,4 @@
* Any virtual-node that may be rendered to DOM
* @typedef {VNodeTree|VNodePrimitive|undefined|Element} VNode
* @category VDOM
**/
**/

View File

@@ -825,6 +825,16 @@
lodash "^4.17.13"
to-fast-properties "^2.0.0"
"@cerxes/host@latest":
version "0.0.1"
resolved "https://npm.cerxes.net:443/@cerxes%2fhost/-/host-0.0.1.tgz#d9a015021196d5aef1eb8b511cd060010c9e0cda"
integrity sha512-iAj137yQQdKDAFagtvwNbwrJNwdf0zHUhT7Uk435GzIA9L6GoOyT9m9/uqMsQFc8AwApx7S4UTu00yzWQduIRQ==
dependencies:
ansi-up "^1.0.0"
json5 "^2.1.1"
minimatch "^3.0.4"
zen-observable latest
"@types/estree@*":
version "0.0.39"
resolved "https://npm.cerxes.net:443/@types%2festree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
@@ -876,6 +886,11 @@ ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
ansi-up@^1.0.0:
version "1.0.0"
resolved "https://npm.cerxes.net:443/ansi-up/-/ansi-up-1.0.0.tgz#235aa25b95f997291f2f6335e95485cae339a356"
integrity sha1-I1qiW5X5lykfL2M16VSFyuM5o1Y=
anymatch@^2.0.0:
version "2.0.0"
resolved "https://npm.cerxes.net:443/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
@@ -1832,6 +1847,13 @@ json5@^2.1.0:
dependencies:
minimist "^1.2.0"
json5@^2.1.1:
version "2.1.3"
resolved "https://npm.cerxes.net:443/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43"
integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==
dependencies:
minimist "^1.2.5"
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
version "3.2.2"
resolved "https://npm.cerxes.net:443/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
@@ -1984,6 +2006,11 @@ minimist@^1.2.0:
resolved "https://npm.cerxes.net:443/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
minimist@^1.2.5:
version "1.2.5"
resolved "https://npm.cerxes.net:443/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
version "2.9.0"
resolved "https://npm.cerxes.net:443/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
@@ -2964,3 +2991,8 @@ yallist@^3.0.0, yallist@^3.0.3:
version "3.1.1"
resolved "https://npm.cerxes.net:443/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
zen-observable@latest:
version "0.8.15"
resolved "https://npm.cerxes.net:443/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15"
integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==