mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
feat: import compiler from marko-js/x
Co-authored-by: Michael Rawlings <mirawlings@ebay.com> Co-authored-by: Dylan Piercey <dpiercey@ebay.com> Co-authored-by: Andrew Gliga <agliga@ebay.com>
This commit is contained in:
parent
ea6736d085
commit
02670c8693
1
.browserslistrc
Normal file
1
.browserslistrc
Normal file
@ -0,0 +1 @@
|
|||||||
|
extends @ebay/browserslist-config
|
||||||
6
.commitlintrc.json
Normal file
6
.commitlintrc.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"extends": [
|
||||||
|
"@commitlint/config-lerna-scopes",
|
||||||
|
"@commitlint/config-conventional"
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -1,18 +1,13 @@
|
|||||||
# minified
|
|
||||||
*.min.js
|
*.min.js
|
||||||
|
*.html.js
|
||||||
# test
|
*.marko.js
|
||||||
*actual*
|
*actual*
|
||||||
*expected*
|
*expected*
|
||||||
input.js
|
.cache/
|
||||||
|
.nyc_output/
|
||||||
# generated
|
node_modules/
|
||||||
*dist*
|
coverage/
|
||||||
*generated*
|
*dist/
|
||||||
*.marko.js
|
*generated/
|
||||||
*.marko.*.js
|
|
||||||
*.html.js
|
|
||||||
~vdom.skip
|
~vdom.skip
|
||||||
coverage
|
**/test/**/input.js
|
||||||
|
|
||||||
node_modules
|
|
||||||
@ -1,11 +1,19 @@
|
|||||||
{
|
{
|
||||||
"extends": ["eslint:recommended", "prettier"],
|
"extends": ["eslint:recommended", "prettier"],
|
||||||
"env": {
|
"parserOptions": {
|
||||||
"node": true,
|
"ecmaVersion": 2019,
|
||||||
"es6": true
|
"sourceType": "module",
|
||||||
|
"ecmaFeatures": {
|
||||||
|
"jsx": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"globals": {
|
"rules": {
|
||||||
"document": true,
|
"no-console": "off"
|
||||||
"ShadowRoot": true
|
},
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"node": true,
|
||||||
|
"es6": true,
|
||||||
|
"mocha": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
133
.gitignore
vendored
133
.gitignore
vendored
@ -1,22 +1,117 @@
|
|||||||
/work
|
### Project ###
|
||||||
/build
|
|
||||||
.idea/
|
|
||||||
npm-debug.log
|
|
||||||
node_modules
|
|
||||||
*.sublime-workspace
|
|
||||||
*.orig
|
|
||||||
.DS_Store
|
|
||||||
.vscode
|
|
||||||
coverage
|
|
||||||
.nvmrc
|
|
||||||
~*
|
|
||||||
/.cache
|
|
||||||
*.marko.js
|
*.marko.js
|
||||||
*.marko.xml.js
|
*actual.*
|
||||||
/test/generated/
|
dist
|
||||||
|
|
||||||
|
### Node ###
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn
|
||||||
|
.yarn-integrity
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
.env.test
|
||||||
|
|
||||||
|
### Git ###
|
||||||
|
|
||||||
|
# Created by git for backups. To disable backups in Git:
|
||||||
|
# $ git config --global mergetool.keepBackup false
|
||||||
|
*.orig
|
||||||
|
|
||||||
|
# Created by git when using merge tools for conflicts
|
||||||
|
*.BACKUP.*
|
||||||
|
*.BASE.*
|
||||||
|
*.LOCAL.*
|
||||||
|
*.REMOTE.*
|
||||||
|
*_BACKUP_*.txt
|
||||||
|
*_BASE_*.txt
|
||||||
|
*_LOCAL_*.txt
|
||||||
|
*_REMOTE_*.txt
|
||||||
|
|
||||||
|
### OS ###
|
||||||
|
|
||||||
|
# Linux
|
||||||
|
*~
|
||||||
|
.fuse_hidden*
|
||||||
|
.directory
|
||||||
|
.Trash-*
|
||||||
|
.nfs*
|
||||||
|
|
||||||
|
# Mac
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
._*
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
*[Tt]humbs*.db*
|
||||||
|
[Dd]esktop.ini
|
||||||
|
*.stackdump
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
### EDITOR ###
|
||||||
|
|
||||||
|
# VisualStudioCode
|
||||||
|
.vscode
|
||||||
|
.history
|
||||||
|
|
||||||
|
# JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
modules.xml
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# SublimeText
|
||||||
|
*.cache
|
||||||
|
*.sublime-*
|
||||||
|
Package Control.*
|
||||||
|
oscrypto-ca-bundle.crt
|
||||||
|
|
||||||
|
# TextMate
|
||||||
|
*.tmproj
|
||||||
|
*.tmproject
|
||||||
|
tmtags
|
||||||
|
|
||||||
|
# Vim
|
||||||
*.swp
|
*.swp
|
||||||
/**/dist/
|
*.vim
|
||||||
/**/test-dist/
|
.netrwhist
|
||||||
/**/test-generated/
|
|
||||||
.vs/
|
|
||||||
|
|||||||
3
.huskyrc
3
.huskyrc
@ -1,7 +1,6 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
"hooks": {
|
"hooks": {
|
||||||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
|
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
|
||||||
"pre-commit": "lint-staged"
|
"pre-commit": "lint-staged"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"*.{json,css,md}": ["prettier --write", "git add"],
|
"*.{json,css,md}": ["prettier --write", "git add"],
|
||||||
"*.js": ["eslint", "prettier --write", "git add"]
|
"*.js": ["eslint", "prettier --write", "git add"]
|
||||||
}
|
}
|
||||||
|
|||||||
18
.npmignore
18
.npmignore
@ -1,18 +0,0 @@
|
|||||||
/test
|
|
||||||
/work
|
|
||||||
/build
|
|
||||||
/.idea/
|
|
||||||
/npm-debug.log
|
|
||||||
/node_modules
|
|
||||||
/*.sublime-workspace
|
|
||||||
*.orig
|
|
||||||
.DS_Store
|
|
||||||
.vscode
|
|
||||||
coverage
|
|
||||||
.nvmrc
|
|
||||||
/benchmark
|
|
||||||
/.cache
|
|
||||||
.nyc_output
|
|
||||||
~*
|
|
||||||
yarn.lock
|
|
||||||
*.afdesign
|
|
||||||
16
.nycrc
Normal file
16
.nycrc
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"all": true,
|
||||||
|
"cache": true,
|
||||||
|
"include": [
|
||||||
|
"packages/*/src/**/*.js"
|
||||||
|
],
|
||||||
|
"instrument": false,
|
||||||
|
"reporter": [
|
||||||
|
"lcov",
|
||||||
|
"text-summary"
|
||||||
|
],
|
||||||
|
"require": [
|
||||||
|
"@babel/register"
|
||||||
|
],
|
||||||
|
"sourceMap": false
|
||||||
|
}
|
||||||
@ -7,17 +7,20 @@
|
|||||||
input.*
|
input.*
|
||||||
|
|
||||||
# generated
|
# generated
|
||||||
/**/dist/
|
dist/
|
||||||
/**/test-dist/
|
|
||||||
/**/test-generated/
|
|
||||||
*.marko.js
|
*.marko.js
|
||||||
*.html.js
|
*.html.js
|
||||||
*.xml.js
|
*.xml.js
|
||||||
*.generated.js
|
*.generated.js
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
.cache/
|
||||||
coverage
|
coverage
|
||||||
~*
|
~*
|
||||||
|
|
||||||
# controlled by npm's formatter
|
# controlled by npm's formatter
|
||||||
package-lock.json
|
package-lock.json
|
||||||
package.json
|
package.json
|
||||||
|
|
||||||
|
# controlled by lerna
|
||||||
|
CHANGELOG.md
|
||||||
|
lerna.json
|
||||||
@ -1,10 +1,8 @@
|
|||||||
sudo: false
|
sudo: false
|
||||||
node_js:
|
node_js:
|
||||||
- "8"
|
|
||||||
- "10"
|
- "10"
|
||||||
- "12"
|
- "12"
|
||||||
language: node_js
|
language: node_js
|
||||||
before_install: "npm i -g npm@latest"
|
|
||||||
install: "npm ci"
|
install: "npm ci"
|
||||||
script: "npm run test-ci"
|
script: "npm run ci:test"
|
||||||
after_success: "npm run codecov"
|
after_success: "npm run ci:codecov"
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright 2016 eBay Inc
|
Copyright 2020 eBay Inc. and contributors
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
35
babel.config.js
Normal file
35
babel.config.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
module.exports = api => ({
|
||||||
|
retainLines: true,
|
||||||
|
presets: [
|
||||||
|
[
|
||||||
|
"@babel/env",
|
||||||
|
{
|
||||||
|
loose: true,
|
||||||
|
targets: {
|
||||||
|
node: "8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
"@babel/transform-runtime",
|
||||||
|
"@babel/plugin-proposal-class-properties",
|
||||||
|
"@babel/plugin-proposal-object-rest-spread"
|
||||||
|
],
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
test: "./packages/*/src/**/*",
|
||||||
|
plugins: api.env("production")
|
||||||
|
? [
|
||||||
|
["babel-plugin-minprops", { matchPrefix: "___", context: "marko" }],
|
||||||
|
"./scripts/babel-plugin-marko-debug"
|
||||||
|
]
|
||||||
|
: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
env: {
|
||||||
|
test: {
|
||||||
|
plugins: ["babel-plugin-istanbul"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"rules": {
|
|
||||||
"no-console": 0
|
|
||||||
},
|
|
||||||
"parserOptions": {
|
|
||||||
"sourceType": "module"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1
benchmark/.gitignore
vendored
1
benchmark/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
static/
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
var nodePath = require("path");
|
|
||||||
|
|
||||||
var Module = require("module").Module;
|
|
||||||
var oldResolveFilename = Module._resolveFilename;
|
|
||||||
|
|
||||||
var rootDir = nodePath.join(__dirname, "../");
|
|
||||||
|
|
||||||
Module._resolveFilename = function(request, parent, isMain) {
|
|
||||||
if (request.charAt(0) !== ".") {
|
|
||||||
var firstSlash = request.indexOf("/");
|
|
||||||
var targetPackageName =
|
|
||||||
firstSlash === -1 ? request : request.substring(0, firstSlash);
|
|
||||||
|
|
||||||
if (targetPackageName === "marko") {
|
|
||||||
request = request.substring("marko".length);
|
|
||||||
request = rootDir + request;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return oldResolveFilename.call(this, request, parent, isMain);
|
|
||||||
};
|
|
||||||
2
benchmark/size/.gitignore
vendored
2
benchmark/size/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
/build/
|
|
||||||
/node_modules/
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import Inferno, { render } from 'inferno';
|
|
||||||
import App from './components/App';
|
|
||||||
|
|
||||||
render(
|
|
||||||
<App name='Frank' colors={['red', 'green', 'blue']}/>,
|
|
||||||
document.body);
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
import Inferno from 'inferno';
|
|
||||||
import Component from 'inferno-component';
|
|
||||||
|
|
||||||
function renderColor(color) {
|
|
||||||
var style = {
|
|
||||||
backgroundColor: color
|
|
||||||
};
|
|
||||||
|
|
||||||
return <li className="color" style={style}>
|
|
||||||
{color}
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderColors(colors) {
|
|
||||||
if (colors.length) {
|
|
||||||
return (<ul>{colors.map(renderColor)}</ul>);
|
|
||||||
} else {
|
|
||||||
return <div>No colors!</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
name: props.name,
|
|
||||||
colors: props.colors,
|
|
||||||
clickCount: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
this.handleButtonClick = function() {
|
|
||||||
this.setState({
|
|
||||||
clickCount: this.state.clickCount + 1
|
|
||||||
});
|
|
||||||
}.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
var colors = this.state.colors;
|
|
||||||
var name = this.state.name;
|
|
||||||
var clickCount = this.state.clickCount;
|
|
||||||
var handleButtonClick = this.handleButtonClick;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1>Hello {name}!</h1>
|
|
||||||
<div className="colors">
|
|
||||||
{renderColors(colors)}
|
|
||||||
</div>
|
|
||||||
<button type="button" onClick={handleButtonClick}>
|
|
||||||
You clicked the button {clickCount} time(s)
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
import browserifyPlugin from "rollup-plugin-browserify-transform";
|
|
||||||
import nodeResolvePlugin from "rollup-plugin-node-resolve";
|
|
||||||
import babelPlugin from "rollup-plugin-babel";
|
|
||||||
import envify from "envify";
|
|
||||||
import path from "path";
|
|
||||||
|
|
||||||
process.env.NODE_ENV = "production";
|
|
||||||
|
|
||||||
// NODE_ENV=production browserify -t envify -t markoify --extension='.marko' --global-transform minprops/browserify -o build/bundles/marko.js marko/client.js
|
|
||||||
|
|
||||||
export default {
|
|
||||||
entry: path.join(__dirname, "client.jsx"),
|
|
||||||
format: "iife",
|
|
||||||
moduleName: "app",
|
|
||||||
plugins: [
|
|
||||||
babelPlugin({
|
|
||||||
include: [],
|
|
||||||
babelrc: false,
|
|
||||||
presets: [["es2015", { loose: true, modules: false }], "stage-0"],
|
|
||||||
plugins: ["inferno"]
|
|
||||||
}),
|
|
||||||
browserifyPlugin(envify),
|
|
||||||
nodeResolvePlugin({
|
|
||||||
jsnext: false, // Default: false
|
|
||||||
main: true, // Default: true
|
|
||||||
browser: true, // Default: false
|
|
||||||
preferBuiltins: false,
|
|
||||||
extensions: [".js", ".jsx"]
|
|
||||||
})
|
|
||||||
],
|
|
||||||
dest: path.join(__dirname, "../build/bundles/inferno.js")
|
|
||||||
};
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Marko</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Marko</h1>
|
|
||||||
<script src="./build/bundles.min/marko.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
var app = require("./components/app");
|
|
||||||
app
|
|
||||||
.renderSync({
|
|
||||||
name: "Frank",
|
|
||||||
colors: ["red", "green", "blue"]
|
|
||||||
})
|
|
||||||
.appendTo(document.body);
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
class {
|
|
||||||
onInput() {
|
|
||||||
this.state = {
|
|
||||||
clickCount: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
handleButtonClick() {
|
|
||||||
this.state.clickCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
div
|
|
||||||
h1 -- Hello ${input.name}!
|
|
||||||
div.colors
|
|
||||||
ul if(input.colors.length)
|
|
||||||
for(color in input.colors)
|
|
||||||
li.color style="background-color: ${color}"
|
|
||||||
-- ${color}
|
|
||||||
div else
|
|
||||||
-- No colors!
|
|
||||||
button type="button" onClick('handleButtonClick')
|
|
||||||
-- You clicked the button ${state.clickCount} time(s)
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
var helpers = require("marko/src/runtime/vdom/helpers");
|
|
||||||
|
|
||||||
console.log("HELPERS:", helpers);
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
process.env.NODE_ENV = "production";
|
|
||||||
|
|
||||||
import commonjsPlugin from "rollup-plugin-commonjs";
|
|
||||||
import browserifyPlugin from "rollup-plugin-browserify-transform";
|
|
||||||
import nodeResolvePlugin from "rollup-plugin-node-resolve";
|
|
||||||
import markoify from "markoify";
|
|
||||||
import envify from "envify";
|
|
||||||
import minpropsify from "minprops/browserify";
|
|
||||||
import path from "path";
|
|
||||||
|
|
||||||
// NODE_ENV=production browserify -t envify -t markoify --extension='.marko' --global-transform minprops/browserify -o build/bundles/marko.js marko/client.js
|
|
||||||
|
|
||||||
export default {
|
|
||||||
entry: path.join(__dirname, "client.js"),
|
|
||||||
format: "iife",
|
|
||||||
moduleName: "app",
|
|
||||||
plugins: [
|
|
||||||
browserifyPlugin(markoify),
|
|
||||||
browserifyPlugin(envify),
|
|
||||||
browserifyPlugin(minpropsify),
|
|
||||||
nodeResolvePlugin({
|
|
||||||
jsnext: false, // Default: false
|
|
||||||
main: true, // Default: true
|
|
||||||
browser: true, // Default: false
|
|
||||||
preferBuiltins: false,
|
|
||||||
extensions: [".js", ".marko"]
|
|
||||||
}),
|
|
||||||
commonjsPlugin({
|
|
||||||
include: [],
|
|
||||||
extensions: [".js", ".marko"]
|
|
||||||
})
|
|
||||||
],
|
|
||||||
dest: path.join(__dirname, "../build/bundles/marko.js")
|
|
||||||
};
|
|
||||||
@ -1,149 +0,0 @@
|
|||||||
console.log("Minifying JavaScript bundles...");
|
|
||||||
|
|
||||||
const fs = require("fs");
|
|
||||||
const path = require("path");
|
|
||||||
const zlib = require("zlib");
|
|
||||||
const UglifyJS = require("uglify-js");
|
|
||||||
const formatNumber = require("format-number")();
|
|
||||||
|
|
||||||
var buildDir = path.join(__dirname, "build");
|
|
||||||
var bundlesDir = path.join(__dirname, "build/bundles");
|
|
||||||
var bundlesMinDir = path.join(__dirname, "build/bundles.min");
|
|
||||||
|
|
||||||
try {
|
|
||||||
fs.mkdirSync(bundlesMinDir);
|
|
||||||
} catch (e) {
|
|
||||||
/* ignore error */
|
|
||||||
}
|
|
||||||
|
|
||||||
var promiseChain = Promise.resolve();
|
|
||||||
|
|
||||||
function getVersion(name) {
|
|
||||||
return require(name + "/package.json").version;
|
|
||||||
}
|
|
||||||
|
|
||||||
function leftPad(str, padding) {
|
|
||||||
if (str.length < padding) {
|
|
||||||
str = new Array(padding - str.length).join(" ") + str;
|
|
||||||
}
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
var minifiers = {
|
|
||||||
gcc: function minifyGCC(src, file) {
|
|
||||||
const gcc = require("google-closure-compiler-js");
|
|
||||||
const options = {
|
|
||||||
jsCode: [{ src: src }],
|
|
||||||
languageIn: "ES5"
|
|
||||||
};
|
|
||||||
|
|
||||||
const out = gcc.compile(options);
|
|
||||||
|
|
||||||
if (out.errors && out.errors.length) {
|
|
||||||
console.error(out.errors);
|
|
||||||
throw new Error(`Minification failed for ${file}`);
|
|
||||||
}
|
|
||||||
return out.compiledCode;
|
|
||||||
},
|
|
||||||
uglify: function minifyUglifyJS(src, file) {
|
|
||||||
try {
|
|
||||||
return UglifyJS.minify(src, {
|
|
||||||
fromString: true
|
|
||||||
}).code;
|
|
||||||
} catch (e) {
|
|
||||||
if (e.line != null) {
|
|
||||||
console.error(`Failed to minify ${file}`);
|
|
||||||
console.error(` Location: ${file}:${e.line}:${e.col}`);
|
|
||||||
console.error(` Message: ${e.message}`);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
both: function(src, file) {
|
|
||||||
var withGCC = minifiers.gcc(src, file);
|
|
||||||
var withBoth = minifiers.uglify(withGCC, file);
|
|
||||||
return withBoth.length < withGCC.length ? withBoth : withGCC;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var minifier = minifiers.both;
|
|
||||||
|
|
||||||
var bundleFiles = fs.readdirSync(bundlesDir);
|
|
||||||
|
|
||||||
var sizes = {};
|
|
||||||
|
|
||||||
var targetLib = process.argv[2];
|
|
||||||
|
|
||||||
bundleFiles.forEach(filename => {
|
|
||||||
if (!filename.endsWith(".js")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var file = path.join(bundlesDir, filename);
|
|
||||||
var ext = path.extname(filename);
|
|
||||||
var lib = filename.slice(0, 0 - ext.length);
|
|
||||||
|
|
||||||
if (targetLib && lib !== targetLib) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Minifying ${file}...`);
|
|
||||||
|
|
||||||
var src = fs.readFileSync(file, { encoding: "utf8" });
|
|
||||||
|
|
||||||
var minifiedSrc = minifier(src, file);
|
|
||||||
|
|
||||||
console.log(`Done minifying ${file}`);
|
|
||||||
|
|
||||||
var minFile = path.join(bundlesMinDir, filename);
|
|
||||||
fs.writeFileSync(minFile, minifiedSrc, { encoding: "utf8" });
|
|
||||||
|
|
||||||
var sizeInfo = (sizes[lib] = {});
|
|
||||||
|
|
||||||
promiseChain = promiseChain.then(() => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
console.log(`Compressing and calculating size of ${file}...`);
|
|
||||||
zlib.gzip(minifiedSrc, function(err, gzippedBuffer) {
|
|
||||||
if (err) {
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare the sizes
|
|
||||||
var minifiedBuffer = new Buffer(minifiedSrc, "utf8");
|
|
||||||
// console.log(nodePath.basename(templateInfo.outputCompileMinifiedFile) + ': ' + gzippedBuffer.length + ' bytes gzipped (' + minifiedBuffer.length + ' bytes uncompressed)');
|
|
||||||
|
|
||||||
sizeInfo.gzipped = gzippedBuffer.length;
|
|
||||||
sizeInfo.min = minifiedBuffer.length;
|
|
||||||
|
|
||||||
var libVersion = getVersion(lib);
|
|
||||||
var sizeFilename = lib + (libVersion ? "-" + libVersion : "") + ".json";
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(buildDir, sizeFilename),
|
|
||||||
JSON.stringify(sizeInfo, null, 4),
|
|
||||||
{ encoding: "utf8" }
|
|
||||||
);
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
promiseChain.then(() => {
|
|
||||||
console.log();
|
|
||||||
|
|
||||||
for (var lib in sizes) {
|
|
||||||
var sizeInfo = sizes[lib];
|
|
||||||
console.log("[" + lib + "]");
|
|
||||||
console.log(
|
|
||||||
" gzip: " + leftPad(formatNumber(sizeInfo.gzipped), 8) + " bytes"
|
|
||||||
);
|
|
||||||
console.log(" min: " + leftPad(formatNumber(sizeInfo.min), 8) + " bytes");
|
|
||||||
console.log();
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Minification complete.");
|
|
||||||
});
|
|
||||||
@ -1,70 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "size-benchmark",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"setup": "npm install --silent && npm link ../../",
|
|
||||||
"build": "npm run bundle --silent && npm run minify --silent",
|
|
||||||
"build-marko": "npm run bundle-marko --silent && node minify.js marko",
|
|
||||||
"build-vue": "npm run bundle-vue --silent && node minify.js vue",
|
|
||||||
"build-react": "npm run bundle-react --silent && node minify.js react",
|
|
||||||
"build-inferno":
|
|
||||||
"npm run bundle-inferno --silent && node minify.js inferno",
|
|
||||||
"bundle":
|
|
||||||
"mkdir -p build/bundles && npm run bundle-marko && npm run bundle-react && npm run bundle-vue && npm run bundle-preact && npm run bundle-inferno",
|
|
||||||
"bundle-marko":
|
|
||||||
"node ../../scripts/build src && NODE_ENV=production rollup -c marko/rollup.config.js",
|
|
||||||
"bundle-react": "NODE_ENV=production rollup -c react/rollup.config.js",
|
|
||||||
"bundle-preact":
|
|
||||||
"NODE_ENV=production rollup -c preact/rollup.config.js",
|
|
||||||
"bundle-vue": "NODE_ENV=production rollup -c vue/rollup.config.js",
|
|
||||||
"bundle-inferno":
|
|
||||||
"NODE_ENV=production rollup -c inferno/rollup.config.js",
|
|
||||||
"minify": "node minify.js",
|
|
||||||
"http-server": "http-server"
|
|
||||||
},
|
|
||||||
"author": "Patrick Steele-Idem <pnidem@gmail.com>",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"babel-plugin-inferno": "^1.8.0",
|
|
||||||
"babel-plugin-transform-es2015-block-scoping": "^6.21.0",
|
|
||||||
"babel-plugin-transform-react-constant-elements": "^6.9.1",
|
|
||||||
"babel-plugin-transform-react-jsx": "^6.8.0",
|
|
||||||
"babel-plugin-transform-runtime": "^6.15.0",
|
|
||||||
"babel-preset-es2015": "^6.18.0",
|
|
||||||
"babel-preset-es2015-loose": "^8.0.0",
|
|
||||||
"babel-preset-react": "^6.16.0",
|
|
||||||
"babel-preset-stage-0": "^6.16.0",
|
|
||||||
"babelify": "^7.3.0",
|
|
||||||
"envify": "^4.0.0",
|
|
||||||
"format-number": "^2.0.1",
|
|
||||||
"google-closure-compiler-js": "^20161201.0.0",
|
|
||||||
"http-server": "^0.9.0",
|
|
||||||
"inferno": "^1.3.0-rc.1",
|
|
||||||
"inferno-component": "^1.3.0-rc.1",
|
|
||||||
"inferno-server": "^1.3.0-rc.1",
|
|
||||||
"markoify": "^2.1.1",
|
|
||||||
"minprops": "^1.0.0",
|
|
||||||
"preact": "^7.1.0",
|
|
||||||
"react": "^15.4.1",
|
|
||||||
"react-dom": "^15.4.1",
|
|
||||||
"rollup": "^0.41.6",
|
|
||||||
"rollup-plugin-babel": "^2.7.1",
|
|
||||||
"rollup-plugin-browserify-transform": "^0.1.0",
|
|
||||||
"rollup-plugin-commonjs": "^8.0.2",
|
|
||||||
"rollup-plugin-marko": "0.0.2",
|
|
||||||
"rollup-plugin-node-resolve": "^3.0.0",
|
|
||||||
"uglify-js": "^2.7.5",
|
|
||||||
"vue": "^2.1.6",
|
|
||||||
"vueify": "^9.4.0"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/marko-js/marko.git"
|
|
||||||
},
|
|
||||||
"browser": {
|
|
||||||
"events": "events-light"
|
|
||||||
},
|
|
||||||
"devDependencies": {}
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Preact</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Preact</h1>
|
|
||||||
<script src="./build/bundles.min/preact.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"presets": [
|
|
||||||
["es2015", { "loose": true, "modules": false }],
|
|
||||||
"stage-0"
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
[ "transform-react-jsx", { "pragma": "h" } ],
|
|
||||||
["transform-es2015-block-scoping"]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
var preact = require('preact');
|
|
||||||
var h = preact.h;
|
|
||||||
var render = preact.render;
|
|
||||||
var App = require('./components/App');
|
|
||||||
|
|
||||||
render(
|
|
||||||
<App name='Frank' colors={['red', 'green', 'blue']}/>,
|
|
||||||
document.body);
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
var preact = require('preact');
|
|
||||||
var h = preact.h;
|
|
||||||
var Component = preact.Component;
|
|
||||||
|
|
||||||
function renderColor(color) {
|
|
||||||
var style = {
|
|
||||||
backgroundColor: color
|
|
||||||
};
|
|
||||||
|
|
||||||
return <li className="color" style={style}>
|
|
||||||
{color}
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderColors(colors) {
|
|
||||||
if (colors.length) {
|
|
||||||
return (<ul>{colors.map(renderColor)}</ul>);
|
|
||||||
} else {
|
|
||||||
return <div>No colors!</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = class extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
name: props.name,
|
|
||||||
colors: props.colors,
|
|
||||||
clickCount: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
this.handleButtonClick = function() {
|
|
||||||
this.setState({
|
|
||||||
clickCount: this.state.clickCount + 1
|
|
||||||
});
|
|
||||||
}.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
var colors = this.state.colors;
|
|
||||||
var name = this.state.name;
|
|
||||||
var clickCount = this.state.clickCount;
|
|
||||||
var handleButtonClick = this.handleButtonClick;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1>Hello {name}!</h1>
|
|
||||||
<div className="colors">
|
|
||||||
{renderColors(colors)}
|
|
||||||
</div>
|
|
||||||
<button type="button" onClick={handleButtonClick}>
|
|
||||||
You clicked the button {clickCount} time(s)
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
import commonjsPlugin from "rollup-plugin-commonjs";
|
|
||||||
import browserifyPlugin from "rollup-plugin-browserify-transform";
|
|
||||||
import nodeResolvePlugin from "rollup-plugin-node-resolve";
|
|
||||||
import babelPlugin from "rollup-plugin-babel";
|
|
||||||
import envify from "envify";
|
|
||||||
import path from "path";
|
|
||||||
|
|
||||||
process.env.NODE_ENV = "production";
|
|
||||||
|
|
||||||
// NODE_ENV=production browserify -t envify -t markoify --extension='.marko' --global-transform minprops/browserify -o build/bundles/marko.js marko/client.js
|
|
||||||
|
|
||||||
export default {
|
|
||||||
entry: path.join(__dirname, "client.jsx"),
|
|
||||||
format: "iife",
|
|
||||||
moduleName: "app",
|
|
||||||
plugins: [
|
|
||||||
babelPlugin({
|
|
||||||
// include: ['node_modules/**', '**/*.js', '**/*.jsx']
|
|
||||||
}),
|
|
||||||
browserifyPlugin(envify),
|
|
||||||
nodeResolvePlugin({
|
|
||||||
jsnext: false, // Default: false
|
|
||||||
main: true, // Default: true
|
|
||||||
browser: true, // Default: false
|
|
||||||
preferBuiltins: false,
|
|
||||||
extensions: [".js", ".jsx"]
|
|
||||||
}),
|
|
||||||
commonjsPlugin({
|
|
||||||
include: [],
|
|
||||||
extensions: [".js", ".jsx"]
|
|
||||||
})
|
|
||||||
],
|
|
||||||
dest: path.join(__dirname, "../build/bundles/preact.js")
|
|
||||||
};
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>React</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>React</h1>
|
|
||||||
<script src="./build/bundles.min/react.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"presets": [
|
|
||||||
["es2015", { "loose": true, "modules": false }],
|
|
||||||
"stage-0",
|
|
||||||
"react"
|
|
||||||
],
|
|
||||||
"plugins": ["transform-react-constant-elements"]
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
var React = require('react');
|
|
||||||
var ReactDOM = require('react-dom');
|
|
||||||
|
|
||||||
var App = require('./components/App');
|
|
||||||
|
|
||||||
ReactDOM.render(
|
|
||||||
<App name='Frank' colors={['red', 'green', 'blue']}/>,
|
|
||||||
document.body);
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
var React = require('react');
|
|
||||||
|
|
||||||
function renderColor(color) {
|
|
||||||
var style = {
|
|
||||||
backgroundColor: color
|
|
||||||
};
|
|
||||||
|
|
||||||
return <li className="color" style={style}>
|
|
||||||
{color}
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderColors(colors) {
|
|
||||||
if (colors.length) {
|
|
||||||
return (<ul>{colors.map(renderColor)}</ul>);
|
|
||||||
} else {
|
|
||||||
return <div>No colors!</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = class extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
name: props.name,
|
|
||||||
colors: props.colors,
|
|
||||||
clickCount: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
this.handleButtonClick = function() {
|
|
||||||
this.setState({
|
|
||||||
clickCount: this.state.clickCount + 1
|
|
||||||
});
|
|
||||||
}.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
var colors = this.state.colors;
|
|
||||||
var name = this.state.name;
|
|
||||||
var clickCount = this.state.clickCount;
|
|
||||||
var handleButtonClick = this.handleButtonClick;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h1>Hello {name}!</h1>
|
|
||||||
<div className="colors">
|
|
||||||
{renderColors(colors)}
|
|
||||||
</div>
|
|
||||||
<button type="button" onClick={handleButtonClick}>
|
|
||||||
You clicked the button {clickCount} time(s)
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
import commonjsPlugin from "rollup-plugin-commonjs";
|
|
||||||
import browserifyPlugin from "rollup-plugin-browserify-transform";
|
|
||||||
import nodeResolvePlugin from "rollup-plugin-node-resolve";
|
|
||||||
import babelPlugin from "rollup-plugin-babel";
|
|
||||||
import envify from "envify";
|
|
||||||
import path from "path";
|
|
||||||
|
|
||||||
process.env.NODE_ENV = "production";
|
|
||||||
|
|
||||||
// NODE_ENV=production browserify -t envify -t markoify --extension='.marko' --global-transform minprops/browserify -o build/bundles/marko.js marko/client.js
|
|
||||||
|
|
||||||
export default {
|
|
||||||
entry: path.join(__dirname, "client.jsx"),
|
|
||||||
format: "iife",
|
|
||||||
moduleName: "app",
|
|
||||||
plugins: [
|
|
||||||
babelPlugin({
|
|
||||||
exclude: "node_modules/**"
|
|
||||||
}),
|
|
||||||
browserifyPlugin(envify),
|
|
||||||
nodeResolvePlugin({
|
|
||||||
jsnext: true, // Default: false
|
|
||||||
main: true, // Default: true
|
|
||||||
browser: true, // Default: false
|
|
||||||
preferBuiltins: false,
|
|
||||||
extensions: [".js", ".jsx"]
|
|
||||||
}),
|
|
||||||
commonjsPlugin({
|
|
||||||
include: [],
|
|
||||||
extensions: [".js", ".jsx"]
|
|
||||||
})
|
|
||||||
],
|
|
||||||
dest: path.join(__dirname, "../build/bundles/react.js")
|
|
||||||
};
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Vue</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Vue</h1>
|
|
||||||
<script src="./build/bundles.min/vue.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
var Vue = require("vue");
|
|
||||||
var App = require("./components/App");
|
|
||||||
|
|
||||||
// var app = new App({
|
|
||||||
// el: document.body,
|
|
||||||
// data: {
|
|
||||||
// name: 'Frank',
|
|
||||||
// colors: ['red', 'green', 'blue']
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
new Vue({
|
|
||||||
el: document.body,
|
|
||||||
render: function(createElement) {
|
|
||||||
return createElement(App);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<h1>
|
|
||||||
Hello {{name}}!
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<div class="colors">
|
|
||||||
<ul v-if="colors.length">
|
|
||||||
<li v-for="color in colors" class="color" v-bind:style="'background-color: ' + color">
|
|
||||||
{{color}}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div v-else>
|
|
||||||
No colors!
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button type="button" v-on:click="clickCount += 1">
|
|
||||||
You clicked the button {{clickCount}} time(s)
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
module.exports = {
|
|
||||||
data: function() {
|
|
||||||
return {
|
|
||||||
clickCount: 0,
|
|
||||||
name: 'Frank',
|
|
||||||
colors: ['red', 'green', 'blue']
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleButtonClick: function() {
|
|
||||||
this.clickCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
import commonjsPlugin from "rollup-plugin-commonjs";
|
|
||||||
import browserifyPlugin from "rollup-plugin-browserify-transform";
|
|
||||||
import nodeResolvePlugin from "rollup-plugin-node-resolve";
|
|
||||||
import vueify from "vueify";
|
|
||||||
import envify from "envify";
|
|
||||||
import minpropsify from "minprops/browserify";
|
|
||||||
import path from "path";
|
|
||||||
|
|
||||||
process.env.NODE_ENV = "production";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
entry: path.join(__dirname, "client.js"),
|
|
||||||
format: "iife",
|
|
||||||
moduleName: "app",
|
|
||||||
plugins: [
|
|
||||||
browserifyPlugin(vueify),
|
|
||||||
browserifyPlugin(envify),
|
|
||||||
browserifyPlugin(minpropsify),
|
|
||||||
nodeResolvePlugin({
|
|
||||||
jsnext: true, // Default: false
|
|
||||||
main: true, // Default: true
|
|
||||||
browser: true, // Default: false
|
|
||||||
preferBuiltins: false,
|
|
||||||
extensions: [".js", ".vue"]
|
|
||||||
}),
|
|
||||||
commonjsPlugin({
|
|
||||||
include: [],
|
|
||||||
extensions: [".js", ".vue"]
|
|
||||||
})
|
|
||||||
],
|
|
||||||
dest: path.join(__dirname, "../build/bundles/vue.js")
|
|
||||||
};
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"env": {
|
|
||||||
"browser": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
module.exports = function(app) {
|
|
||||||
var Suite = window.Benchmark.Suite;
|
|
||||||
|
|
||||||
var names = ["dom", "dom-innerHTML", "marko-vdom", "react"];
|
|
||||||
|
|
||||||
var htmlFiles = ["todomvc", "marko-docs", "tabs"];
|
|
||||||
|
|
||||||
function loadScripts() {
|
|
||||||
window.createBenchmarks = {};
|
|
||||||
|
|
||||||
var scripts = [];
|
|
||||||
|
|
||||||
names.forEach(function(name) {
|
|
||||||
htmlFiles.forEach(function(htmlFile) {
|
|
||||||
scripts.push(
|
|
||||||
"./codegen-create/benchmark-" + htmlFile + "-" + name + ".js"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return app.loadScripts(scripts);
|
|
||||||
}
|
|
||||||
|
|
||||||
function runForHtmlFile(htmlFile) {
|
|
||||||
return loadScripts(htmlFile).then(function() {
|
|
||||||
var suite = new Suite("create-" + htmlFile);
|
|
||||||
|
|
||||||
names.forEach(function(name) {
|
|
||||||
suite.add(name, function() {
|
|
||||||
return window.createBenchmarks[htmlFile + "-" + name]();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return app.runSuite(suite);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var loadScriptsPromise = loadScripts();
|
|
||||||
|
|
||||||
return function() {
|
|
||||||
var promiseChain = loadScriptsPromise;
|
|
||||||
|
|
||||||
htmlFiles.forEach(function(htmlFile) {
|
|
||||||
promiseChain = promiseChain.then(function() {
|
|
||||||
return runForHtmlFile(htmlFile);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return promiseChain;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,93 +0,0 @@
|
|||||||
module.exports = function(app) {
|
|
||||||
var Suite = window.Benchmark.Suite;
|
|
||||||
var MarkoVDOM = app.vdom;
|
|
||||||
|
|
||||||
var suite = new Suite("walk");
|
|
||||||
|
|
||||||
var todomvcDOM = document.getElementById("todoapp");
|
|
||||||
var todomvcDOMVirtual = MarkoVDOM.virtualize(todomvcDOM);
|
|
||||||
|
|
||||||
function toHTML(node) {
|
|
||||||
// NOTE: We don't use XMLSerializer because we need to sort the attributes to correctly compare output HTML strings
|
|
||||||
// BAD: return (new XMLSerializer()).serializeToString(node);
|
|
||||||
var html = "";
|
|
||||||
function serializeHelper(node, indent) {
|
|
||||||
if (node.nodeType === 1) {
|
|
||||||
serializeElHelper(node, indent);
|
|
||||||
} else if (node.nodeType === 3) {
|
|
||||||
serializeTextHelper(node, indent);
|
|
||||||
} else if (node.nodeType === 8) {
|
|
||||||
serializeCommentHelper(node, indent);
|
|
||||||
} else {
|
|
||||||
console.log("Invalid node:", node);
|
|
||||||
html += indent + `INVALID NODE TYPE ${node.nodeType}\n`;
|
|
||||||
// throw new Error('Unexpected node type');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function serializeElHelper(el, indent) {
|
|
||||||
var tagName = el.nodeName;
|
|
||||||
|
|
||||||
if (el.namespaceURI === "http://www.w3.org/2000/svg") {
|
|
||||||
tagName = "svg:" + tagName;
|
|
||||||
} else if (el.namespaceURI === "http://www.w3.org/1998/Math/MathML") {
|
|
||||||
tagName = "math:" + tagName;
|
|
||||||
}
|
|
||||||
|
|
||||||
html += indent + "<" + tagName;
|
|
||||||
|
|
||||||
var attributes = el.attributes;
|
|
||||||
var attributesArray = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < attributes.length; i++) {
|
|
||||||
var attr = attributes[i];
|
|
||||||
var attrName = attr.name;
|
|
||||||
if (attr.namespaceURI) {
|
|
||||||
attrName = attr.namespaceURI + ":" + attrName;
|
|
||||||
}
|
|
||||||
attributesArray.push(" " + attrName + '="' + attr.value + '"');
|
|
||||||
}
|
|
||||||
|
|
||||||
attributesArray.sort();
|
|
||||||
|
|
||||||
html += attributesArray.join("");
|
|
||||||
|
|
||||||
html += ">\n";
|
|
||||||
|
|
||||||
if (tagName.toUpperCase() === "TEXTAREA") {
|
|
||||||
html += indent + " VALUE: " + JSON.stringify(el.value) + "\n";
|
|
||||||
} else {
|
|
||||||
var curChild = el.firstChild;
|
|
||||||
while (curChild) {
|
|
||||||
serializeHelper(curChild, indent + " ");
|
|
||||||
curChild = curChild.nextSibling;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function serializeTextHelper(node, indent) {
|
|
||||||
html += indent + JSON.stringify(node.nodeValue) + "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
function serializeCommentHelper(node, indent) {
|
|
||||||
html += indent + "<!--" + JSON.stringify(node.nodeValue) + "-->\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
serializeHelper(node, "");
|
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add tests
|
|
||||||
suite.add("real DOM", function() {
|
|
||||||
return toHTML(todomvcDOM);
|
|
||||||
});
|
|
||||||
|
|
||||||
suite.add("marko-vdom", function() {
|
|
||||||
return toHTML(todomvcDOMVirtual);
|
|
||||||
});
|
|
||||||
|
|
||||||
return function() {
|
|
||||||
return app.runSuite(suite);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"dependencies": [
|
|
||||||
{
|
|
||||||
"type": "js",
|
|
||||||
"url": "https://unpkg.com/react@15.3.1/dist/react.min.js"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "js",
|
|
||||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.js"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "js",
|
|
||||||
"url": "https://cdnjs.cloudflare.com/ajax/libs/benchmark/2.1.2/benchmark.js"
|
|
||||||
},
|
|
||||||
"benchmark/benchmark.js",
|
|
||||||
"require-run: ./client.js"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,127 +0,0 @@
|
|||||||
var HTMLElement = require("../../runtime/vdom/HTMLElement");
|
|
||||||
var Text = require("../../runtime/vdom/Text");
|
|
||||||
var Comment = require("../../runtime/vdom/Comment");
|
|
||||||
var DocumentFragment = require("../../runtime/vdom/DocumentFragment");
|
|
||||||
|
|
||||||
var resultsEl = document.getElementById("results");
|
|
||||||
var running = false;
|
|
||||||
var benchmarks = {};
|
|
||||||
|
|
||||||
function loadScript(path) {
|
|
||||||
return new Promise(function(resolve, reject) {
|
|
||||||
var script = document.createElement("script");
|
|
||||||
script.src = path;
|
|
||||||
script.onload = function() {
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
|
|
||||||
script.onerror = function(e) {
|
|
||||||
reject(e);
|
|
||||||
};
|
|
||||||
|
|
||||||
document.head.appendChild(script); //or something of the likes
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadScripts(paths) {
|
|
||||||
return Promise.all(
|
|
||||||
paths.map(function(path) {
|
|
||||||
return loadScript(path);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function runSuite(suite) {
|
|
||||||
return new Promise(function(resolve, reject) {
|
|
||||||
if (running) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
running = true;
|
|
||||||
|
|
||||||
suite
|
|
||||||
.on("start", function() {
|
|
||||||
resultsEl.innerHTML += 'Running "' + suite.name + '"...\n';
|
|
||||||
})
|
|
||||||
.on("cycle", function(event) {
|
|
||||||
resultsEl.innerHTML += String(event.target) + "\n";
|
|
||||||
})
|
|
||||||
.on("complete", function() {
|
|
||||||
resultsEl.innerHTML +=
|
|
||||||
"Fastest is " +
|
|
||||||
this.filter("fastest").map("name") +
|
|
||||||
"\n\n--------------\n\n";
|
|
||||||
|
|
||||||
running = false;
|
|
||||||
|
|
||||||
suite.off("start cycle complete");
|
|
||||||
resolve();
|
|
||||||
})
|
|
||||||
.on("error", function(e) {
|
|
||||||
running = false;
|
|
||||||
|
|
||||||
suite.off("start cycle complete error");
|
|
||||||
reject(e.target.error);
|
|
||||||
})
|
|
||||||
// run async
|
|
||||||
.run({ async: true });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var vdom = (window.MarkoVDOM = {
|
|
||||||
virtualize: require("../../runtime/vdom/virtualize"),
|
|
||||||
|
|
||||||
createElement: function(tagName, attrs, childCount, constId) {
|
|
||||||
return new HTMLElement(tagName, attrs, childCount, constId);
|
|
||||||
},
|
|
||||||
createText: function(value) {
|
|
||||||
return new Text(value);
|
|
||||||
},
|
|
||||||
createComment: function(value) {
|
|
||||||
return new Comment(value);
|
|
||||||
},
|
|
||||||
createDocumentFragment: function() {
|
|
||||||
return new DocumentFragment();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var app = {
|
|
||||||
loadScript,
|
|
||||||
loadScripts,
|
|
||||||
runSuite,
|
|
||||||
vdom
|
|
||||||
};
|
|
||||||
|
|
||||||
function registerBenchmark(name, func) {
|
|
||||||
benchmarks[name] = func(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
registerBenchmark("create", require("./benchmark-create"));
|
|
||||||
registerBenchmark("walk", require("./benchmark-walk"));
|
|
||||||
|
|
||||||
document.body.addEventListener("click", function(event) {
|
|
||||||
if (running) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var target = event.target;
|
|
||||||
var benchmarkName = target.getAttribute("data-benchmark");
|
|
||||||
if (benchmarkName) {
|
|
||||||
var oldButtonLabel = target.innerHTML;
|
|
||||||
target.innerHTML = oldButtonLabel + " - running...";
|
|
||||||
resultsEl.innerHTML = "";
|
|
||||||
|
|
||||||
var benchmarkFunc = benchmarks[benchmarkName];
|
|
||||||
|
|
||||||
benchmarkFunc()
|
|
||||||
.then(function() {
|
|
||||||
target.innerHTML = oldButtonLabel;
|
|
||||||
|
|
||||||
resultsEl.innerHTML += "\nDONE!";
|
|
||||||
})
|
|
||||||
.catch(function(e) {
|
|
||||||
target.innerHTML = oldButtonLabel;
|
|
||||||
console.error(e);
|
|
||||||
resultsEl.innerHTML = e.toString();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
1
benchmark/vdom/codegen-create/.gitignore
vendored
1
benchmark/vdom/codegen-create/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
benchmark-*
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
module.exports = function() {
|
|
||||||
return `
|
|
||||||
var fragment = range.createContextualFragment(html);
|
|
||||||
return fragment.childNodes[0];`;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.generateInitCode = function(node, html) {
|
|
||||||
return `
|
|
||||||
var range = document.createRange();
|
|
||||||
range.selectNode(document.body);
|
|
||||||
var html = ${JSON.stringify(html)};
|
|
||||||
`;
|
|
||||||
};
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
module.exports = function(node) {
|
|
||||||
var nextId = 0;
|
|
||||||
|
|
||||||
var code = "";
|
|
||||||
|
|
||||||
function codegenEl(node, level) {
|
|
||||||
var varName = level === 0 ? "root" : `node${nextId++}`;
|
|
||||||
|
|
||||||
code += `var ${varName} = document.createElement(${JSON.stringify(
|
|
||||||
node.nodeName
|
|
||||||
)})\n`;
|
|
||||||
|
|
||||||
var attributes = node.attributes;
|
|
||||||
|
|
||||||
for (var i = 0; i < attributes.length; i++) {
|
|
||||||
var attr = attributes[i];
|
|
||||||
code += `${varName}.setAttribute(${JSON.stringify(
|
|
||||||
attr.name
|
|
||||||
)}, ${JSON.stringify(attr.value)})\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
var curChild = node.firstChild;
|
|
||||||
while (curChild) {
|
|
||||||
if (curChild.nodeType === 1) {
|
|
||||||
var childVarName = codegenEl(curChild, level + 1);
|
|
||||||
code += `${varName}.appendChild(${childVarName})\n`;
|
|
||||||
} else if (curChild.nodeType === 3) {
|
|
||||||
code += `${varName}.appendChild(document.createTextNode(${JSON.stringify(
|
|
||||||
curChild.nodeValue
|
|
||||||
)}))\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
curChild = curChild.nextSibling;
|
|
||||||
}
|
|
||||||
|
|
||||||
return varName;
|
|
||||||
}
|
|
||||||
|
|
||||||
codegenEl(node, 0);
|
|
||||||
|
|
||||||
code += "\nreturn root;\n";
|
|
||||||
|
|
||||||
return code;
|
|
||||||
};
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
function indentStr(level) {
|
|
||||||
var str = "";
|
|
||||||
for (var i = 0; i < level; i++) {
|
|
||||||
str += " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
module.exports = function(node) {
|
|
||||||
var code = "";
|
|
||||||
|
|
||||||
function codegenEl(node, level) {
|
|
||||||
if (level === 0) {
|
|
||||||
code += "createElement";
|
|
||||||
} else {
|
|
||||||
code += ".e";
|
|
||||||
}
|
|
||||||
|
|
||||||
var attrsMap = {};
|
|
||||||
|
|
||||||
var attributes = node.attributes;
|
|
||||||
for (var i = 0; i < attributes.length; i++) {
|
|
||||||
var attr = attributes[i];
|
|
||||||
attrsMap[attr.name] = attr.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
code += `(${JSON.stringify(node.nodeName)}, ${JSON.stringify(attrsMap)}, ${
|
|
||||||
node.childNodes.length
|
|
||||||
})\n`;
|
|
||||||
// codegenAttrs(node.attributes, level + 1);
|
|
||||||
|
|
||||||
var curChild = node.firstChild;
|
|
||||||
while (curChild) {
|
|
||||||
codegen(curChild, level + 1);
|
|
||||||
curChild = curChild.nextSibling;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function codegenText(node) {
|
|
||||||
code += `.t(${JSON.stringify(node.nodeValue)})\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function codegen(node, level) {
|
|
||||||
code += indentStr(level);
|
|
||||||
|
|
||||||
if (node.nodeType === 1) {
|
|
||||||
codegenEl(node, level);
|
|
||||||
} else if (node.nodeType === 3) {
|
|
||||||
codegenText(node, level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
codegen(node, 0);
|
|
||||||
|
|
||||||
return "return " + code + "\n";
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.generateInitCode = function() {
|
|
||||||
return `
|
|
||||||
var MarkoVDOM = window.MarkoVDOM;
|
|
||||||
var createElement = MarkoVDOM.createElement;
|
|
||||||
`;
|
|
||||||
};
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
function indentStr(level) {
|
|
||||||
var str = "";
|
|
||||||
for (var i = 0; i < level; i++) {
|
|
||||||
str += " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function(node) {
|
|
||||||
function codegenChildNodes(node, level) {
|
|
||||||
var curChild = node.firstChild;
|
|
||||||
if (!curChild) {
|
|
||||||
return "null";
|
|
||||||
}
|
|
||||||
|
|
||||||
var code = "[\n";
|
|
||||||
|
|
||||||
var childLevel = level + 2;
|
|
||||||
while (curChild) {
|
|
||||||
code += indentStr(childLevel) + codegen(curChild, childLevel);
|
|
||||||
curChild = curChild.nextSibling;
|
|
||||||
|
|
||||||
if (curChild) {
|
|
||||||
code += ",\n";
|
|
||||||
} else {
|
|
||||||
code += "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
code += indentStr(level + 1) + "]";
|
|
||||||
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
function codegenEl(node, level) {
|
|
||||||
var attributesMap = null;
|
|
||||||
|
|
||||||
var attributes = node.attributes;
|
|
||||||
if (attributes.length) {
|
|
||||||
attributesMap = {};
|
|
||||||
|
|
||||||
for (var i = 0; i < attributes.length; i++) {
|
|
||||||
var attr = attributes[i];
|
|
||||||
var attrName = attr.name;
|
|
||||||
var attrValue = attr.value;
|
|
||||||
|
|
||||||
if (attrName === "class") {
|
|
||||||
attrName = "className";
|
|
||||||
}
|
|
||||||
attributesMap[attrName] = attrValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return `React.createElement(${JSON.stringify(
|
|
||||||
node.nodeName
|
|
||||||
)}, ${JSON.stringify(attributesMap)}, ${codegenChildNodes(node, level)})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function codegen(node, level) {
|
|
||||||
if (node.nodeType === 1) {
|
|
||||||
return codegenEl(node, level);
|
|
||||||
} else if (node.nodeType === 3) {
|
|
||||||
return JSON.stringify(node.nodeValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "return " + codegen(node, 0) + "\n";
|
|
||||||
};
|
|
||||||
File diff suppressed because one or more lines are too long
@ -1,19 +0,0 @@
|
|||||||
<div>
|
|
||||||
|
|
||||||
<!-- Nav tabs -->
|
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
|
||||||
<li role="presentation" class="active"><a href="#home" aria-controls="home" role="tab" data-toggle="tab">Home</a></li>
|
|
||||||
<li role="presentation"><a href="#profile" aria-controls="profile" role="tab" data-toggle="tab">Profile</a></li>
|
|
||||||
<li role="presentation"><a href="#messages" aria-controls="messages" role="tab" data-toggle="tab">Messages</a></li>
|
|
||||||
<li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">Settings</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<!-- Tab panes -->
|
|
||||||
<div class="tab-content">
|
|
||||||
<div role="tabpanel" class="tab-pane active" id="home">...</div>
|
|
||||||
<div role="tabpanel" class="tab-pane" id="profile">...</div>
|
|
||||||
<div role="tabpanel" class="tab-pane" id="messages">...</div>
|
|
||||||
<div role="tabpanel" class="tab-pane" id="settings">...</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
@ -1,82 +0,0 @@
|
|||||||
<section id="todoapp">
|
|
||||||
<div id="w0">
|
|
||||||
<header id="header">
|
|
||||||
<h1>todos</h1>
|
|
||||||
<form data-w-onsubmit="handleFormSubmit|header">
|
|
||||||
<input id="new-todo" placeholder="What needs to be done?">
|
|
||||||
</form>
|
|
||||||
</header>
|
|
||||||
<section id="main">
|
|
||||||
<input id="toggle-all" type="checkbox" data-w-onchange="handleToggleAllOnChange|main">
|
|
||||||
<label for="toggle-all">Mark all as complete</label>
|
|
||||||
<ul id="todo-list">
|
|
||||||
<li id="main-todo-0" class="completed">
|
|
||||||
<div class="view">
|
|
||||||
<input class="toggle" type="checkbox" aria-label="Toggle todo completed" data-w-onchange="handleCheckboxChange|main-todo-0" checked="">
|
|
||||||
<label data-w-ondblclick="handleLabelDblClick|main-todo-0">Go to the grocery store FOO</label>
|
|
||||||
<button class="destroy" aria-label="Delete todo" data-w-onclick="handleDestroyClick|main-todo-0"></button>
|
|
||||||
</div>
|
|
||||||
<input title="Enter the new todo title" type="text" class="edit" id="main-todo-0-titleInput" data-w-onchange="handleInputChange|main-todo-0" data-w-onkeydown="handleInputKeyDown|main-todo-0">
|
|
||||||
</li>
|
|
||||||
<li id="main-todo-1">
|
|
||||||
<div class="view">
|
|
||||||
<input class="toggle" type="checkbox" aria-label="Toggle todo completed" data-w-onchange="handleCheckboxChange|main-todo-1">
|
|
||||||
<label data-w-ondblclick="handleLabelDblClick|main-todo-1">Ship item</label>
|
|
||||||
<button class="destroy" aria-label="Delete todo" data-w-onclick="handleDestroyClick|main-todo-1"></button>
|
|
||||||
</div>
|
|
||||||
<input title="Enter the new todo title" type="text" class="edit" id="main-todo-1-titleInput" data-w-onchange="handleInputChange|main-todo-1" data-w-onkeydown="handleInputKeyDown|main-todo-1">
|
|
||||||
</li>
|
|
||||||
<li id="main-todo-2">
|
|
||||||
<div class="view">
|
|
||||||
<input class="toggle" type="checkbox" aria-label="Toggle todo completed" data-w-onchange="handleCheckboxChange|main-todo-2">
|
|
||||||
<label data-w-ondblclick="handleLabelDblClick|main-todo-2">Respond to email</label>
|
|
||||||
<button class="destroy" aria-label="Delete todo" data-w-onclick="handleDestroyClick|main-todo-2"></button>
|
|
||||||
</div>
|
|
||||||
<input title="Enter the new todo title" type="text" class="edit" id="main-todo-2-titleInput" data-w-onchange="handleInputChange|main-todo-2" data-w-onkeydown="handleInputKeyDown|main-todo-2">
|
|
||||||
</li>
|
|
||||||
<li id="main-todo-3">
|
|
||||||
<div class="view">
|
|
||||||
<input class="toggle" type="checkbox" aria-label="Toggle todo completed" data-w-onchange="handleCheckboxChange|main-todo-3">
|
|
||||||
<label data-w-ondblclick="handleLabelDblClick|main-todo-3">Foo</label>
|
|
||||||
<button class="destroy" aria-label="Delete todo" data-w-onclick="handleDestroyClick|main-todo-3"></button>
|
|
||||||
</div>
|
|
||||||
<input title="Enter the new todo title" type="text" class="edit" id="main-todo-3-titleInput" data-w-onchange="handleInputChange|main-todo-3" data-w-onkeydown="handleInputKeyDown|main-todo-3">
|
|
||||||
</li>
|
|
||||||
<li id="main-todo-4" class="completed">
|
|
||||||
<div class="view">
|
|
||||||
<input class="toggle" type="checkbox" aria-label="Toggle todo completed" data-w-onchange="handleCheckboxChange|main-todo-4" checked="">
|
|
||||||
<label data-w-ondblclick="handleLabelDblClick|main-todo-4">Bar</label>
|
|
||||||
<button class="destroy" aria-label="Delete todo" data-w-onclick="handleDestroyClick|main-todo-4"></button>
|
|
||||||
</div>
|
|
||||||
<input title="Enter the new todo title" type="text" class="edit" id="main-todo-4-titleInput" data-w-onchange="handleInputChange|main-todo-4" data-w-onkeydown="handleInputKeyDown|main-todo-4">
|
|
||||||
</li>
|
|
||||||
<li id="main-todo-5">
|
|
||||||
<div class="view">
|
|
||||||
<input class="toggle" type="checkbox" aria-label="Toggle todo completed" data-w-onchange="handleCheckboxChange|main-todo-5">
|
|
||||||
<label data-w-ondblclick="handleLabelDblClick|main-todo-5">Baz</label>
|
|
||||||
<button class="destroy" aria-label="Delete todo" data-w-onclick="handleDestroyClick|main-todo-5"></button>
|
|
||||||
</div>
|
|
||||||
<input title="Enter the new todo title" type="text" class="edit" id="main-todo-5-titleInput" data-w-onchange="handleInputChange|main-todo-5" data-w-onkeydown="handleInputKeyDown|main-todo-5">
|
|
||||||
</li>
|
|
||||||
<li id="main-todo-6">
|
|
||||||
<div class="view">
|
|
||||||
<input class="toggle" type="checkbox" aria-label="Toggle todo completed" data-w-onchange="handleCheckboxChange|main-todo-6">
|
|
||||||
<label data-w-ondblclick="handleLabelDblClick|main-todo-6">Test</label>
|
|
||||||
<button class="destroy" aria-label="Delete todo" data-w-onclick="handleDestroyClick|main-todo-6"></button>
|
|
||||||
</div>
|
|
||||||
<input title="Enter the new todo title" type="text" class="edit" id="main-todo-6-titleInput" data-w-onchange="handleInputChange|main-todo-6" data-w-onkeydown="handleInputKeyDown|main-todo-6">
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<footer id="footer">
|
|
||||||
<span id="todo-count">
|
|
||||||
<strong>5</strong> items left</span>
|
|
||||||
<ul id="filters">
|
|
||||||
<li><a href="#/" class="selected" data-w-onclick="handleAllFilterClick|footer">All</a></li>
|
|
||||||
<li><a href="#/active" data-w-onclick="handleActiveFilterClick|footer">Active</a></li>
|
|
||||||
<li><a href="#/completed" data-w-onclick="handleCompletedFilterClick|footer">Completed</a></li>
|
|
||||||
</ul>
|
|
||||||
<button id="clear-completed" data-w-onclick="handleClearCompletedClick|footer">Clear completed (2)</button>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
var jsdom = require("jsdom").jsdom;
|
|
||||||
var fs = require("fs");
|
|
||||||
var path = require("path");
|
|
||||||
|
|
||||||
function generateCode(name, htmlFile, rootNode, html) {
|
|
||||||
var generator = require("./codegen-" + name);
|
|
||||||
|
|
||||||
var code = generator(rootNode, html);
|
|
||||||
var wrappedCode = `window.createBenchmarks[${JSON.stringify(
|
|
||||||
htmlFile + "-" + name
|
|
||||||
)}]=function() {\n${code}\n}`;
|
|
||||||
|
|
||||||
var generateInitCode = generator.generateInitCode;
|
|
||||||
|
|
||||||
if (generateInitCode) {
|
|
||||||
wrappedCode = `
|
|
||||||
(function() {
|
|
||||||
${generateInitCode(rootNode, html)}
|
|
||||||
${wrappedCode}
|
|
||||||
}())`;
|
|
||||||
}
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(__dirname, `benchmark-${htmlFile}-${name}.js`),
|
|
||||||
wrappedCode,
|
|
||||||
{ encoding: "utf8" }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
var methods = ["dom", "dom-innerHTML", "marko-vdom", "react"];
|
|
||||||
|
|
||||||
var htmlFiles = fs.readdirSync(__dirname).filter(function(name) {
|
|
||||||
return name.startsWith("html-");
|
|
||||||
});
|
|
||||||
|
|
||||||
htmlFiles.forEach(function(htmlFile) {
|
|
||||||
var name = htmlFile.substring("html-".length).slice(0, 0 - ".html".length);
|
|
||||||
var html = fs.readFileSync(path.join(__dirname, htmlFile), {
|
|
||||||
encoding: "utf8"
|
|
||||||
});
|
|
||||||
|
|
||||||
var doc = jsdom(html);
|
|
||||||
|
|
||||||
var rootNode = doc.body.firstChild;
|
|
||||||
|
|
||||||
methods.forEach(function(methodName) {
|
|
||||||
generateCode(methodName, name, rootNode, html);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,98 +0,0 @@
|
|||||||
<lasso-page package-path="./browser.json"/>
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>marko-vdom benchmarks</title>
|
|
||||||
<lasso-head/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<button type="button" data-benchmark="walk">
|
|
||||||
Run benchmark: walk
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button type="button" data-benchmark="create">
|
|
||||||
Run benchmark: create
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<pre id="results" style="width: 100%; border: 1px solid black;">
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<section id="todoapp" style="display: none">
|
|
||||||
<div id="w0">
|
|
||||||
<header id="header">
|
|
||||||
<h1>todos</h1>
|
|
||||||
<form data-w-onsubmit="handleFormSubmit|header">
|
|
||||||
<input id="new-todo" placeholder="What needs to be done?">
|
|
||||||
</form>
|
|
||||||
</header>
|
|
||||||
<section id="main">
|
|
||||||
<input id="toggle-all" type="checkbox" data-w-onchange="handleToggleAllOnChange|main">
|
|
||||||
<label for="toggle-all">Mark all as complete</label>
|
|
||||||
<ul id="todo-list">
|
|
||||||
<li id="main-todo-0">
|
|
||||||
<div class="view">
|
|
||||||
<input class="toggle" type="checkbox" aria-label="Toggle todo completed" data-w-onchange="handleCheckboxChange|main-todo-0">
|
|
||||||
<label data-w-ondblclick="handleLabelDblClick|main-todo-0">Go to the grocery store</label>
|
|
||||||
<button class="destroy" aria-label="Delete todo" data-w-onclick="handleDestroyClick|main-todo-0"></button>
|
|
||||||
</div>
|
|
||||||
<input title="Enter the new todo title" type="text" class="edit" id="main-todo-0-titleInput" data-w-onchange="handleInputChange|main-todo-0" data-w-onkeydown="handleInputKeyDown|main-todo-0">
|
|
||||||
</li>
|
|
||||||
<li class="completed" id="main-todo-1">
|
|
||||||
<div class="view">
|
|
||||||
<input class="toggle" type="checkbox" checked="" aria-label="Toggle todo completed" data-w-onchange="handleCheckboxChange|main-todo-1">
|
|
||||||
<label data-w-ondblclick="handleLabelDblClick|main-todo-1">Ship item</label>
|
|
||||||
<button class="destroy" aria-label="Delete todo" data-w-onclick="handleDestroyClick|main-todo-1"></button>
|
|
||||||
</div>
|
|
||||||
<input title="Enter the new todo title" type="text" class="edit" id="main-todo-1-titleInput" data-w-onchange="handleInputChange|main-todo-1" data-w-onkeydown="handleInputKeyDown|main-todo-1">
|
|
||||||
</li>
|
|
||||||
<li id="main-todo-2">
|
|
||||||
<div class="view">
|
|
||||||
<input class="toggle" type="checkbox" aria-label="Toggle todo completed" data-w-onchange="handleCheckboxChange|main-todo-2">
|
|
||||||
<label data-w-ondblclick="handleLabelDblClick|main-todo-2">Respond to email</label>
|
|
||||||
<button class="destroy" aria-label="Delete todo" data-w-onclick="handleDestroyClick|main-todo-2"></button>
|
|
||||||
</div>
|
|
||||||
<input title="Enter the new todo title" type="text" class="edit" id="main-todo-2-titleInput" data-w-onchange="handleInputChange|main-todo-2" data-w-onkeydown="handleInputKeyDown|main-todo-2">
|
|
||||||
</li>
|
|
||||||
<li id="main-todo-3">
|
|
||||||
<div class="view">
|
|
||||||
<input class="toggle" type="checkbox" aria-label="Toggle todo completed" data-w-onchange="handleCheckboxChange|main-todo-3">
|
|
||||||
<label data-w-ondblclick="handleLabelDblClick|main-todo-3">Foo</label>
|
|
||||||
<button class="destroy" aria-label="Delete todo" data-w-onclick="handleDestroyClick|main-todo-3"></button>
|
|
||||||
</div>
|
|
||||||
<input title="Enter the new todo title" type="text" class="edit" id="main-todo-3-titleInput" data-w-onchange="handleInputChange|main-todo-3" data-w-onkeydown="handleInputKeyDown|main-todo-3">
|
|
||||||
</li>
|
|
||||||
<li id="main-todo-4">
|
|
||||||
<div class="view">
|
|
||||||
<input class="toggle" type="checkbox" aria-label="Toggle todo completed" data-w-onchange="handleCheckboxChange|main-todo-4">
|
|
||||||
<label data-w-ondblclick="handleLabelDblClick|main-todo-4">Bar</label>
|
|
||||||
<button class="destroy" aria-label="Delete todo" data-w-onclick="handleDestroyClick|main-todo-4"></button>
|
|
||||||
</div>
|
|
||||||
<input title="Enter the new todo title" type="text" class="edit" id="main-todo-4-titleInput" data-w-onchange="handleInputChange|main-todo-4" data-w-onkeydown="handleInputKeyDown|main-todo-4">
|
|
||||||
</li>
|
|
||||||
<li id="main-todo-5">
|
|
||||||
<div class="view">
|
|
||||||
<input class="toggle" type="checkbox" aria-label="Toggle todo completed" data-w-onchange="handleCheckboxChange|main-todo-5">
|
|
||||||
<label data-w-ondblclick="handleLabelDblClick|main-todo-5">Baz</label>
|
|
||||||
<button class="destroy" aria-label="Delete todo" data-w-onclick="handleDestroyClick|main-todo-5"></button>
|
|
||||||
</div>
|
|
||||||
<input title="Enter the new todo title" type="text" class="edit" id="main-todo-5-titleInput" data-w-onchange="handleInputChange|main-todo-5" data-w-onkeydown="handleInputKeyDown|main-todo-5">
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<footer id="footer">
|
|
||||||
<span id="todo-count">
|
|
||||||
<strong>5</strong> items left</span>
|
|
||||||
<ul id="filters">
|
|
||||||
<li><a href="#/" class="selected" data-w-onclick="handleAllFilterClick|footer">All</a></li>
|
|
||||||
<li><a href="#/active" data-w-onclick="handleActiveFilterClick|footer">Active</a></li>
|
|
||||||
<li><a href="#/completed" data-w-onclick="handleCompletedFilterClick|footer">Completed</a></li>
|
|
||||||
</ul>
|
|
||||||
<button id="clear-completed" data-w-onclick="handleClearCompletedClick|footer">Clear completed (1)</button>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<lasso-body/>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
require("../patch-module");
|
|
||||||
|
|
||||||
require("marko/node-require");
|
|
||||||
require("marko/express");
|
|
||||||
|
|
||||||
var isProduction = process.env.NODE_ENV === "production";
|
|
||||||
|
|
||||||
require("lasso").configure({
|
|
||||||
outputDir: __dirname + "/static",
|
|
||||||
bundlingEnabled: isProduction,
|
|
||||||
fingerprintsEnabled: isProduction,
|
|
||||||
minify: isProduction
|
|
||||||
});
|
|
||||||
|
|
||||||
var express = require("express");
|
|
||||||
|
|
||||||
var app = express();
|
|
||||||
|
|
||||||
var serveStatic = require("serve-static");
|
|
||||||
|
|
||||||
require("./codegen-create/run");
|
|
||||||
|
|
||||||
var template = require("./index.marko");
|
|
||||||
|
|
||||||
app.use("/codegen-create", serveStatic(__dirname + "/codegen-create"));
|
|
||||||
|
|
||||||
app.use(require("lasso/middleware").serveStatic());
|
|
||||||
|
|
||||||
app.get("/", function(req, res) {
|
|
||||||
res.marko(template);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(8080, function(err) {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Server ready:\nhttp://localhost:8080");
|
|
||||||
});
|
|
||||||
@ -1,3 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
extends: ["@commitlint/config-conventional"]
|
extends: [
|
||||||
|
"@commitlint/config-conventional",
|
||||||
|
"@commitlint/config-lerna-scopes"
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
17
lerna.json
Normal file
17
lerna.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"command": {
|
||||||
|
"publish": {
|
||||||
|
"conventionalCommits": true,
|
||||||
|
"message": "chore(release): publish"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"packages": [
|
||||||
|
"packages/*"
|
||||||
|
],
|
||||||
|
"ignoreChanges": [
|
||||||
|
"**/package-lock.json",
|
||||||
|
"packages/*/test/**",
|
||||||
|
"**/*.md"
|
||||||
|
],
|
||||||
|
"version": "independent"
|
||||||
|
}
|
||||||
20217
package-lock.json
generated
20217
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
100
package.json
100
package.json
@ -1,49 +1,55 @@
|
|||||||
{
|
{
|
||||||
"name": "marko",
|
"name": "marko",
|
||||||
"version": "4.0.0",
|
"private": true,
|
||||||
"private": true,
|
"scripts": {
|
||||||
"scripts": {
|
"build": "lerna exec --parallel -- babel ./src --out-dir ./dist --delete-dir-on-start --copy-files --config-file ../../babel.config.js --source-maps=inline --env-name=production",
|
||||||
"build": "node scripts/build.js",
|
"build:watch": "npm run build -- --watch",
|
||||||
"build-src": "node scripts/build.js src",
|
"postinstall": "lerna bootstrap --hoist --no-ci",
|
||||||
"postinstall": "cd packages/marko && npm i",
|
"postrelease": "ENTRY=main:dev npm run set-entry",
|
||||||
"release": "npm run build-src && (cd ./packages/marko && standard-version && git push --follow-tags && npm publish)",
|
"prerelease": "ENTRY=main:npm npm run set-entry",
|
||||||
"test": "mocha --timeout 10000 ./packages/marko/test/*/*.test.js",
|
"release": "npm run build && lerna publish",
|
||||||
"test-ci": "npm run lint && npm run check-format && npm run test-generate-coverage",
|
"test": "mocha -r @babel/register \"packages/*/test/{*.test.js,*/*.test.js}\"",
|
||||||
"test-coverage": "npm run test-generate-coverage && nyc report --reporter=html && opn ./coverage/index.html",
|
"test:watch": "npm run mocha -- --recursive --watch",
|
||||||
"test-generate-coverage": "nyc -asc npm test",
|
"clean": "lerna clean && rm -rf ./packages/*/dist",
|
||||||
"lint": "eslint .",
|
"lint": "eslint -f visualstudio .",
|
||||||
"format": "prettier \"**/*.{js,json,css,md}\" --write",
|
"format": "prettier \"**/*.{json,md,js}\" --write",
|
||||||
"check-format": "prettier \"**/*.{js,json,css,md}\" -l",
|
"check-format": "prettier \"**/*.{js,json,css,md}\" -l",
|
||||||
"codecov": "nyc report --reporter=text-lcov > coverage.lcov && codecov"
|
"ci:test": "npm run lint && npm run check-format && cross-env MARKO_DEBUG=1 NODE_ENV=test nyc --reporter=text npm test",
|
||||||
},
|
"ci:codecov": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
|
||||||
"devDependencies": {
|
"report": "nyc report --reporter=html && open ./coverage/index.html",
|
||||||
"@commitlint/cli": "^8.3.5",
|
"set-entry": "lerna exec -- dot-json package.json main $\\(dot-json package.json $ENTRY\\)"
|
||||||
"@commitlint/config-conventional": "^8.3.4",
|
},
|
||||||
"babel-cli": "^6.24.1",
|
"devDependencies": {
|
||||||
"babel-core": "^6.24.1",
|
"@babel/cli": "^7.7.7",
|
||||||
"babel-plugin-minprops": "^2.0.1",
|
"@babel/core": "^7.7.7",
|
||||||
"benchmark": "^2.1.1",
|
"@babel/helper-module-imports": "^7.7.4",
|
||||||
"codecov": "^3.0.2",
|
"@babel/node": "^7.7.7",
|
||||||
"eslint": "^4.11.0",
|
"@babel/plugin-proposal-class-properties": "^7.7.4",
|
||||||
"eslint-config-prettier": "^2.9.0",
|
"@babel/plugin-proposal-export-default-from": "^7.7.4",
|
||||||
"husky": "^4.2.3",
|
"@babel/plugin-proposal-export-namespace-from": "^7.7.4",
|
||||||
"lint-staged": "^7.0.0",
|
"@babel/plugin-proposal-object-rest-spread": "^7.7.7",
|
||||||
"micromatch": "^3.0.4",
|
"@babel/plugin-transform-runtime": "^7.7.6",
|
||||||
"mocha": "^5.0.1",
|
"@babel/preset-env": "^7.7.7",
|
||||||
"nyc": "^13.0.0",
|
"@babel/register": "^7.7.7",
|
||||||
"prettier": "^1.13.5",
|
"@commitlint/cli": "^8.2.0",
|
||||||
"shelljs": "^0.7.7",
|
"@commitlint/config-conventional": "^8.2.0",
|
||||||
"standard-version": "^7.1.0"
|
"@commitlint/config-lerna-scopes": "^8.2.0",
|
||||||
},
|
"@ebay/browserslist-config": "^1.0.1",
|
||||||
"nyc": {
|
"babel-plugin-istanbul": "^6.0.0",
|
||||||
"exclude": [
|
"babel-plugin-minprops": "^2.0.1",
|
||||||
"**/benchmark/**",
|
"chai": "^4.2.0",
|
||||||
"**/scripts/**",
|
"codecov": "^3.6.1",
|
||||||
"**/coverage/**",
|
"cross-env": "^6.0.3",
|
||||||
"**/test/**",
|
"dot-json": "^1.1.0",
|
||||||
"**/test-dist/**",
|
"eslint": "^6.8.0",
|
||||||
"**/test-generated/**",
|
"eslint-config-prettier": "^6.10.0",
|
||||||
"**/dist/**"
|
"husky": "^3.1.0",
|
||||||
]
|
"it-fails": "^1.0.4",
|
||||||
}
|
"lerna": "^3.19.0",
|
||||||
|
"lint-staged": "^9.5.0",
|
||||||
|
"mocha": "^5.2.0",
|
||||||
|
"mocha-autotest": "^1.0.3",
|
||||||
|
"nyc": "^15.0.0",
|
||||||
|
"prettier": "^1.19.1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
39
packages/babel-types/package.json
Normal file
39
packages/babel-types/package.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"name": "@marko/babel-types",
|
||||||
|
"description": "Extend babels built in AST with Marko nodes.",
|
||||||
|
"version": "5.0.0",
|
||||||
|
"author": "Dylan Piercey <dpiercey@ebay.com>",
|
||||||
|
"bugs": "https://github.com/marko-js/marko/issues/new?template=Bug_report.md",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/generator": "^7",
|
||||||
|
"@babel/traverse": "^7",
|
||||||
|
"@babel/types": "^7"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.7.7",
|
||||||
|
"self-closing-tags": "^1.0.1"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/marko-js/marko/blob/master/packages/babel-types/README.md",
|
||||||
|
"keywords": [
|
||||||
|
"htmljs",
|
||||||
|
"parser",
|
||||||
|
"babel",
|
||||||
|
"plugin",
|
||||||
|
"parse",
|
||||||
|
"marko"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "src/index.js",
|
||||||
|
"main:dev": "src/index.js",
|
||||||
|
"main:npm": "dist/index.js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/marko-js/marko/tree/master/packages/babel-types"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
}
|
||||||
|
}
|
||||||
114
packages/babel-types/src/definitions/index.js
Normal file
114
packages/babel-types/src/definitions/index.js
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
import * as babelTypes from "@babel/types";
|
||||||
|
import { NodePath, Scope } from "@babel/traverse";
|
||||||
|
import builder from "@babel/types/lib/builders/builder";
|
||||||
|
import defineType from "@babel/types/lib/definitions/utils";
|
||||||
|
import * as generatedValidators from "@babel/types/lib/validators/generated";
|
||||||
|
import * as referencedValidators from "@babel/types/lib/validators/isReferenced";
|
||||||
|
import types from "./types";
|
||||||
|
|
||||||
|
const {
|
||||||
|
TYPES,
|
||||||
|
VISITOR_KEYS,
|
||||||
|
FLIPPED_ALIAS_KEYS,
|
||||||
|
DEPRECATED_KEYS,
|
||||||
|
is
|
||||||
|
} = babelTypes;
|
||||||
|
|
||||||
|
const aliases = {};
|
||||||
|
export const MARKO_TYPES = Object.keys(types);
|
||||||
|
MARKO_TYPES.forEach(typeName => {
|
||||||
|
const type = types[typeName];
|
||||||
|
for (const alias of type.aliases) {
|
||||||
|
aliases[alias] = aliases[alias] || [];
|
||||||
|
aliases[alias].push(typeName);
|
||||||
|
}
|
||||||
|
defineType(typeName, type);
|
||||||
|
});
|
||||||
|
const ALIAS_TYPES = Object.keys(aliases);
|
||||||
|
|
||||||
|
// Update TYPES
|
||||||
|
for (const type of [
|
||||||
|
...Object.keys(VISITOR_KEYS),
|
||||||
|
...Object.keys(FLIPPED_ALIAS_KEYS),
|
||||||
|
...Object.keys(DEPRECATED_KEYS)
|
||||||
|
]) {
|
||||||
|
if (!TYPES.includes(type)) TYPES.push(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add marko validators & builders to `@babel/types` and `@babel/traverse`
|
||||||
|
MARKO_TYPES.forEach(typeName => {
|
||||||
|
const lowerName = typeName[0].toLowerCase() + typeName.slice(1);
|
||||||
|
const checkKey = `is${typeName}`;
|
||||||
|
const assertKey = `assert${typeName}`;
|
||||||
|
const checkFn = (babelTypes[checkKey] = (node, opts) =>
|
||||||
|
is(typeName, node, opts));
|
||||||
|
const assertFn = (babelTypes[assertKey] = (node, opts) =>
|
||||||
|
assert(typeName, node, opts));
|
||||||
|
NodePath.prototype[checkKey] = function(opts) {
|
||||||
|
return checkFn(this.node, opts);
|
||||||
|
};
|
||||||
|
NodePath.prototype[assertKey] = function(opts) {
|
||||||
|
assertFn(this.node, opts);
|
||||||
|
};
|
||||||
|
// Add builder.
|
||||||
|
babelTypes[typeName] = babelTypes[lowerName] = (...args) =>
|
||||||
|
builder(typeName, ...args);
|
||||||
|
});
|
||||||
|
|
||||||
|
ALIAS_TYPES.forEach(aliasName => {
|
||||||
|
const checkKey = `is${aliasName}`;
|
||||||
|
const originalCheck = generatedValidators[checkKey];
|
||||||
|
generatedValidators[checkKey] = (node, opts) => {
|
||||||
|
return is(aliasName, node, opts) || originalCheck(node, opts);
|
||||||
|
};
|
||||||
|
const originalProtoCheck = NodePath.prototype[checkKey];
|
||||||
|
NodePath.prototype[checkKey] = function(opts) {
|
||||||
|
return (
|
||||||
|
is(aliasName, this.node, opts) ||
|
||||||
|
originalProtoCheck.call(this, this.node, opts)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const originalIsReferenced = referencedValidators.default;
|
||||||
|
referencedValidators.default = (node, parent, grandparent) => {
|
||||||
|
if (
|
||||||
|
parent.type === "MarkoTag" &&
|
||||||
|
parent.params &&
|
||||||
|
parent.params.includes(node)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return originalIsReferenced(node, parent, grandparent);
|
||||||
|
};
|
||||||
|
|
||||||
|
const originalCrawl = Scope.prototype.crawl;
|
||||||
|
Scope.prototype.crawl = function() {
|
||||||
|
const path = this.path;
|
||||||
|
|
||||||
|
originalCrawl.apply(this, arguments);
|
||||||
|
|
||||||
|
if (path.isMarkoTagBody()) {
|
||||||
|
const params = path.parentPath.get("params");
|
||||||
|
|
||||||
|
if (params.length) {
|
||||||
|
for (const param of params) {
|
||||||
|
this.registerBinding("param", param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function assert(typeName, node, opts) {
|
||||||
|
if (!is(typeName, node, opts)) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected type "${typeName}" with option ${JSON.stringify(
|
||||||
|
opts
|
||||||
|
)}, but instead got "${node.type}".`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// export babel stuff
|
||||||
|
Object.assign(exports, babelTypes);
|
||||||
|
export * from "@babel/types";
|
||||||
206
packages/babel-types/src/definitions/types.js
Normal file
206
packages/babel-types/src/definitions/types.js
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
import {
|
||||||
|
assertNodeType,
|
||||||
|
assertValueType,
|
||||||
|
arrayOfType,
|
||||||
|
chain,
|
||||||
|
assertEach
|
||||||
|
} from "@babel/types/lib/definitions/utils";
|
||||||
|
|
||||||
|
import { functionCommon } from "@babel/types/lib/definitions/core";
|
||||||
|
const valueFieldCommon = {
|
||||||
|
value: {
|
||||||
|
validate: assertValueType("string")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
MarkoDocumentType: {
|
||||||
|
aliases: ["Marko", "Statement"],
|
||||||
|
builder: ["value"],
|
||||||
|
visitor: ["value"],
|
||||||
|
fields: { ...valueFieldCommon }
|
||||||
|
},
|
||||||
|
|
||||||
|
MarkoDeclaration: {
|
||||||
|
aliases: ["Marko", "Statement"],
|
||||||
|
builder: ["value"],
|
||||||
|
visitor: ["value"],
|
||||||
|
fields: { ...valueFieldCommon }
|
||||||
|
},
|
||||||
|
|
||||||
|
MarkoCDATA: {
|
||||||
|
aliases: ["Marko", "Statement"],
|
||||||
|
builder: ["value"],
|
||||||
|
visitor: ["value"],
|
||||||
|
fields: { ...valueFieldCommon }
|
||||||
|
},
|
||||||
|
|
||||||
|
MarkoComment: {
|
||||||
|
aliases: ["Marko", "Statement"],
|
||||||
|
builder: ["value"],
|
||||||
|
visitor: ["value"],
|
||||||
|
fields: { ...valueFieldCommon }
|
||||||
|
},
|
||||||
|
|
||||||
|
MarkoText: {
|
||||||
|
aliases: ["Marko", "Statement"],
|
||||||
|
builder: ["value"],
|
||||||
|
visitor: ["value"],
|
||||||
|
fields: { ...valueFieldCommon }
|
||||||
|
},
|
||||||
|
|
||||||
|
MarkoPlaceholder: {
|
||||||
|
aliases: ["Marko", "Statement"],
|
||||||
|
builder: ["value", "escape"],
|
||||||
|
visitor: ["value"],
|
||||||
|
fields: {
|
||||||
|
value: {
|
||||||
|
validate: assertNodeType("Expression")
|
||||||
|
},
|
||||||
|
escape: {
|
||||||
|
validate: assertValueType("boolean"),
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
MarkoScriptlet: {
|
||||||
|
aliases: ["Marko", "Statement"],
|
||||||
|
builder: ["body", "static"],
|
||||||
|
visitor: ["body"],
|
||||||
|
fields: {
|
||||||
|
body: {
|
||||||
|
validate: arrayOfType(["Statement"])
|
||||||
|
},
|
||||||
|
static: {
|
||||||
|
validate: assertValueType("boolean"),
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
MarkoClass: {
|
||||||
|
aliases: ["Marko", "Statement"],
|
||||||
|
builder: ["body"],
|
||||||
|
visitor: ["body"],
|
||||||
|
fields: {
|
||||||
|
body: {
|
||||||
|
validate: assertNodeType("ClassBody")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
MarkoAttribute: {
|
||||||
|
aliases: ["Marko", "Expression"],
|
||||||
|
builder: ["name", "value", "modifier", "arguments"],
|
||||||
|
visitor: ["value", "arguments"],
|
||||||
|
fields: {
|
||||||
|
name: {
|
||||||
|
validate: assertValueType("string")
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
validate: assertNodeType("Expression"),
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
modifier: {
|
||||||
|
validate: assertValueType("string"),
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
arguments: {
|
||||||
|
validate: chain(
|
||||||
|
assertValueType("array"),
|
||||||
|
assertEach(assertNodeType("Expression", "SpreadElement"))
|
||||||
|
),
|
||||||
|
optional: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
MarkoSpreadAttribute: {
|
||||||
|
aliases: ["Marko", "Expression"],
|
||||||
|
builder: ["value"],
|
||||||
|
visitor: ["value"],
|
||||||
|
fields: {
|
||||||
|
value: {
|
||||||
|
validate: assertNodeType("Expression"),
|
||||||
|
optional: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
MarkoTagBody: {
|
||||||
|
aliases: ["Marko", "Block", "BlockParent", "Scope", "Scopable"],
|
||||||
|
builder: ["body"],
|
||||||
|
visitor: ["body"],
|
||||||
|
fields: {
|
||||||
|
body: {
|
||||||
|
validate: arrayOfType([
|
||||||
|
"MarkoTag",
|
||||||
|
"MarkoCDATA",
|
||||||
|
"MarkoText",
|
||||||
|
"MarkoPlaceholder",
|
||||||
|
"MarkoScriptlet",
|
||||||
|
"MarkoComment"
|
||||||
|
]),
|
||||||
|
default: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
MarkoTag: {
|
||||||
|
aliases: ["Marko", "Statement"],
|
||||||
|
builder: [
|
||||||
|
"name",
|
||||||
|
"attributes",
|
||||||
|
"body",
|
||||||
|
"params",
|
||||||
|
"arguments",
|
||||||
|
"properties",
|
||||||
|
"runtimeFlags"
|
||||||
|
],
|
||||||
|
visitor: ["name", "attributes", "body", "params", "arguments"],
|
||||||
|
fields: {
|
||||||
|
name: {
|
||||||
|
validate: assertNodeType("Expression")
|
||||||
|
},
|
||||||
|
attributes: {
|
||||||
|
validate: arrayOfType(["MarkoAttribute", "MarkoSpreadAttribute"]),
|
||||||
|
default: []
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
validate: assertNodeType("MarkoTagBody")
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
...functionCommon.params,
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
arguments: {
|
||||||
|
validate: chain(
|
||||||
|
assertValueType("array"),
|
||||||
|
assertEach(assertNodeType("Expression", "SpreadElement"))
|
||||||
|
),
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
validate: arrayOfType(["ObjectProperty"]),
|
||||||
|
default: []
|
||||||
|
},
|
||||||
|
handlers: {
|
||||||
|
validate: assertEach(assertNodeType("Expression")),
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
rawValue: {
|
||||||
|
validate: assertValueType("string"),
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
runtimeFlags: {
|
||||||
|
validate: assertValueType("number"),
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
key: {
|
||||||
|
validate: assertNodeType("Expression"),
|
||||||
|
optional: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
205
packages/babel-types/src/generators/index.js
Normal file
205
packages/babel-types/src/generators/index.js
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
import * as t from "../definitions";
|
||||||
|
import SELF_CLOSING from "self-closing-tags";
|
||||||
|
import Printer from "@babel/generator/lib/printer";
|
||||||
|
|
||||||
|
const UNENCLOSED_WHITESPACE_TYPES = [
|
||||||
|
"LogicalExpression",
|
||||||
|
"AssignmentExpression",
|
||||||
|
"ConditionalExpression",
|
||||||
|
"BinaryExpression",
|
||||||
|
"NewExpression",
|
||||||
|
"Function",
|
||||||
|
"AssignmentExpression"
|
||||||
|
];
|
||||||
|
|
||||||
|
Object.assign(Printer.prototype, {
|
||||||
|
MarkoDocumentType(node) {
|
||||||
|
this.token("<!");
|
||||||
|
this.token(node.value);
|
||||||
|
this.token(">");
|
||||||
|
},
|
||||||
|
MarkoDeclaration(node) {
|
||||||
|
this.token("<?");
|
||||||
|
this.token(node.value);
|
||||||
|
this.token("?>");
|
||||||
|
},
|
||||||
|
MarkoCDATA(node) {
|
||||||
|
this.token("<![CDATA[");
|
||||||
|
this.token(node.value);
|
||||||
|
this.token("]]>");
|
||||||
|
},
|
||||||
|
MarkoComment(node) {
|
||||||
|
this.token("<!--");
|
||||||
|
this.token(node.value);
|
||||||
|
this.token("-->");
|
||||||
|
},
|
||||||
|
MarkoPlaceholder(node, parent) {
|
||||||
|
const parentBody = parent.body;
|
||||||
|
const prev = parentBody[parentBody.indexOf(node) - 1];
|
||||||
|
|
||||||
|
if (prev && (t.isMarkoText(prev) || t.isMarkoPlaceholder(prev))) {
|
||||||
|
this.removeTrailingNewline();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.token(node.escape ? "${" : "$!{");
|
||||||
|
this.print(node.value, node);
|
||||||
|
this.token("}");
|
||||||
|
},
|
||||||
|
MarkoScriptlet(node, parent) {
|
||||||
|
this.removeTrailingNewline();
|
||||||
|
|
||||||
|
if (!(t.isProgram(parent) && parent.body.indexOf(node) === 0)) {
|
||||||
|
this.token("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.token(`${node.static ? "static" : "$"} `);
|
||||||
|
|
||||||
|
if (node.body.length === 1) {
|
||||||
|
// TODO should determine if node has unenclosed newlines.
|
||||||
|
this.print(node.body[0], node);
|
||||||
|
} else {
|
||||||
|
this.token("{");
|
||||||
|
this.newline();
|
||||||
|
this.indent();
|
||||||
|
this.printSequence(node.body, node);
|
||||||
|
this.dedent();
|
||||||
|
this.token("}");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MarkoClass(node) {
|
||||||
|
this.token("class");
|
||||||
|
this.token(" ");
|
||||||
|
this.print(node.body, node);
|
||||||
|
},
|
||||||
|
MarkoAttribute(node) {
|
||||||
|
this.token(node.name);
|
||||||
|
|
||||||
|
if (node.modifier) {
|
||||||
|
this.token(":");
|
||||||
|
this.token(node.modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.arguments && node.arguments.length) {
|
||||||
|
this.token("(");
|
||||||
|
this.printList(node.arguments, node);
|
||||||
|
this.token(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!t.isBooleanLiteral(node.value) || !node.value.value) {
|
||||||
|
this.token("=");
|
||||||
|
printWithParansIfNeeded.call(this, node.value, node);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MarkoSpreadAttribute(node) {
|
||||||
|
this.token("...");
|
||||||
|
printWithParansIfNeeded.call(this, node.value, node);
|
||||||
|
},
|
||||||
|
MarkoText(node, parent) {
|
||||||
|
const parentBody = parent.body;
|
||||||
|
const prev = parentBody[parentBody.indexOf(node) - 1];
|
||||||
|
const concatToPrev = prev && t.isMarkoPlaceholder(prev);
|
||||||
|
let { value } = node;
|
||||||
|
|
||||||
|
if (concatToPrev) {
|
||||||
|
this.removeTrailingNewline();
|
||||||
|
}
|
||||||
|
|
||||||
|
const isMultiLine = /[\r\n]/g.test(value);
|
||||||
|
const isRootLevel = !concatToPrev && t.isProgram(parent);
|
||||||
|
|
||||||
|
if (isRootLevel) {
|
||||||
|
if (isMultiLine) {
|
||||||
|
this.token("---\n");
|
||||||
|
} else {
|
||||||
|
this.token("-- ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.word(value);
|
||||||
|
|
||||||
|
if (isMultiLine && isRootLevel) {
|
||||||
|
this.token("\n---");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MarkoTag(node) {
|
||||||
|
const isDynamicTag = !t.isStringLiteral(node.name);
|
||||||
|
const tagName = !isDynamicTag && node.name.value;
|
||||||
|
const selfClosing =
|
||||||
|
!node.body.body.length || SELF_CLOSING.includes(tagName);
|
||||||
|
const rawValue = node.rawValue;
|
||||||
|
|
||||||
|
if (
|
||||||
|
tagName === "style" &&
|
||||||
|
/^style(?:\.[^\s]+)?\s*\{[\s\S]*}$/.test(rawValue)
|
||||||
|
) {
|
||||||
|
this.token(rawValue);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.token("<");
|
||||||
|
|
||||||
|
if (rawValue) {
|
||||||
|
this.token(rawValue);
|
||||||
|
} else {
|
||||||
|
if (isDynamicTag) {
|
||||||
|
this.token("${");
|
||||||
|
this.print(node.name, node);
|
||||||
|
this.token("}");
|
||||||
|
} else {
|
||||||
|
this.token(tagName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.arguments && node.arguments.length) {
|
||||||
|
this.token("(");
|
||||||
|
this.printList(node.arguments, node);
|
||||||
|
this.token(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.params && node.params.length) {
|
||||||
|
this.token("|");
|
||||||
|
this.printList(node.params, node);
|
||||||
|
this.token("|");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.attributes.length) {
|
||||||
|
this.token(" ");
|
||||||
|
this.printJoin(node.attributes, node, { separator: spaceSeparator });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selfClosing) {
|
||||||
|
this.token("/>");
|
||||||
|
} else {
|
||||||
|
this.token(">");
|
||||||
|
this.newline();
|
||||||
|
this.printSequence(node.body.body, node.body, { indent: true });
|
||||||
|
this.token("</");
|
||||||
|
if (!isDynamicTag) {
|
||||||
|
this.token(tagName);
|
||||||
|
}
|
||||||
|
this.token(">");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function spaceSeparator() {
|
||||||
|
this.token(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
function printWithParansIfNeeded(value, parent) {
|
||||||
|
const needsParans = hasUnenclosedWhitespace(value);
|
||||||
|
|
||||||
|
if (needsParans) {
|
||||||
|
this.token("(");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.print(value, parent);
|
||||||
|
|
||||||
|
if (needsParans) {
|
||||||
|
this.token(")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasUnenclosedWhitespace(node) {
|
||||||
|
return UNENCLOSED_WHITESPACE_TYPES.includes(node.type);
|
||||||
|
}
|
||||||
3
packages/babel-types/src/index.js
Normal file
3
packages/babel-types/src/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import "./generators";
|
||||||
|
import * as types from "./definitions";
|
||||||
|
export { types };
|
||||||
35
packages/babel-utils/package.json
Normal file
35
packages/babel-utils/package.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "@marko/babel-utils",
|
||||||
|
"description": "Utilities for use with Marko babel plugins.",
|
||||||
|
"version": "5.0.0",
|
||||||
|
"author": "Dylan Piercey <dpiercey@ebay.com>",
|
||||||
|
"bugs": "https://github.com/marko-js/marko/issues/new?template=Bug_report.md",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.7.7",
|
||||||
|
"@marko/babel-types": "^5.0.0",
|
||||||
|
"jsesc": "^2.5.2"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/marko-js/marko/blob/master/packages/babel-utils/README.md",
|
||||||
|
"keywords": [
|
||||||
|
"htmljs",
|
||||||
|
"parser",
|
||||||
|
"babel",
|
||||||
|
"plugin",
|
||||||
|
"parse",
|
||||||
|
"marko"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "src/index.js",
|
||||||
|
"main:dev": "src/index.js",
|
||||||
|
"main:npm": "dist/index.js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/marko-js/marko/tree/master/packages/babel-utils"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
}
|
||||||
|
}
|
||||||
45
packages/babel-utils/src/assert.js
Normal file
45
packages/babel-utils/src/assert.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
export function assertAllowedAttributes(path, allowed) {
|
||||||
|
const { node } = path;
|
||||||
|
node.attributes.forEach((attr, i) => {
|
||||||
|
if (!allowed.includes(attr.name)) {
|
||||||
|
throw path
|
||||||
|
.get(`attributes.${i}`)
|
||||||
|
.buildCodeFrameError(
|
||||||
|
`Invalid "${node.name.value}" tag attribute: "${attr.name}".`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertNoAttributes(path) {
|
||||||
|
assertAllowedAttributes(path, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertNoParams(path) {
|
||||||
|
const { hub } = path;
|
||||||
|
const params = path.get("params");
|
||||||
|
if (params.length) {
|
||||||
|
const start = params[0].node.start;
|
||||||
|
const end = params[params.length - 1].node.end;
|
||||||
|
throw hub.buildError({ start, end }, "Tag does not support parameters.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertNoAttributeTags(path) {
|
||||||
|
const exampleAttributeTag = path.get("exampleAttributeTag");
|
||||||
|
if (exampleAttributeTag.node) {
|
||||||
|
throw exampleAttributeTag
|
||||||
|
.get("name")
|
||||||
|
.buildCodeFrameError("@tags must be within a custom element.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assertNoArgs(path) {
|
||||||
|
const { hub } = path;
|
||||||
|
const args = path.get("arguments");
|
||||||
|
if (args.length) {
|
||||||
|
const start = args[0].node.start;
|
||||||
|
const end = args[args.length - 1].node.end;
|
||||||
|
throw hub.buildError({ start, end }, "Tag does not support arguments.");
|
||||||
|
}
|
||||||
|
}
|
||||||
21
packages/babel-utils/src/index.js
Normal file
21
packages/babel-utils/src/index.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
export {
|
||||||
|
isNativeTag,
|
||||||
|
isMacroTag,
|
||||||
|
isDynamicTag,
|
||||||
|
isAttributeTag,
|
||||||
|
isTransparentTag,
|
||||||
|
getMacroIdentifier,
|
||||||
|
getTagDef,
|
||||||
|
getFullyResolvedTagName,
|
||||||
|
findParentTag,
|
||||||
|
findAttributeTags,
|
||||||
|
getArgOrSequence
|
||||||
|
} from "./tags";
|
||||||
|
export {
|
||||||
|
assertAllowedAttributes,
|
||||||
|
assertNoArgs,
|
||||||
|
assertNoAttributes,
|
||||||
|
assertNoParams,
|
||||||
|
assertNoAttributeTags
|
||||||
|
} from "./assert";
|
||||||
|
export { normalizeTemplateString } from "./template-string";
|
||||||
123
packages/babel-utils/src/tags.js
Normal file
123
packages/babel-utils/src/tags.js
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import { types as t } from "@marko/babel-types";
|
||||||
|
const transparentTags = new Set(["for", "while", "if", "else", "_no-update"]);
|
||||||
|
|
||||||
|
export function isNativeTag(path) {
|
||||||
|
const tagDef = path.node.tagDef;
|
||||||
|
return (
|
||||||
|
tagDef &&
|
||||||
|
tagDef.html &&
|
||||||
|
(tagDef.htmlType === "custom-element" ||
|
||||||
|
(!tagDef.template && !tagDef.renderer))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isDynamicTag(path) {
|
||||||
|
return !path.get("name").isStringLiteral();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAttributeTag(path) {
|
||||||
|
return !isDynamicTag(path) && path.get("name.value").node[0] === "@";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isTransparentTag(path) {
|
||||||
|
return (
|
||||||
|
!isDynamicTag(path) && transparentTags.has(path.get("name.value").node)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isMacroTag(path) {
|
||||||
|
return Boolean(getMacroIdentifier(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMacroIdentifier(path) {
|
||||||
|
return !isDynamicTag(path) && path.hub.macros[path.get("name.value").node];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTagDef(path) {
|
||||||
|
const cached = path.get("tagDef");
|
||||||
|
|
||||||
|
if (cached.node !== undefined) {
|
||||||
|
return cached.node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { hub } = path;
|
||||||
|
const { lookup } = hub;
|
||||||
|
let tagName;
|
||||||
|
|
||||||
|
if (!(isMacroTag(path) || isDynamicTag(path))) {
|
||||||
|
tagName = isAttributeTag(path)
|
||||||
|
? getFullyResolvedTagName(path)
|
||||||
|
: path.get("name.value").node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagDef = (tagName && lookup.getTag(tagName)) || null;
|
||||||
|
path.set("tagDef", tagDef);
|
||||||
|
return tagDef;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFullyResolvedTagName(path) {
|
||||||
|
const parts = [];
|
||||||
|
let cur;
|
||||||
|
do {
|
||||||
|
cur = path.node.name.value;
|
||||||
|
|
||||||
|
if (isAttributeTag(path)) {
|
||||||
|
parts.push(cur.slice(1));
|
||||||
|
} else {
|
||||||
|
parts.push(cur || "*");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while ((path = findParentTag(path)));
|
||||||
|
|
||||||
|
return parts.reverse().join(":");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findParentTag(path) {
|
||||||
|
let cur = path.parentPath;
|
||||||
|
|
||||||
|
while (cur.node) {
|
||||||
|
if (cur.isMarkoTagBody()) {
|
||||||
|
cur = cur.parentPath;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cur.isMarkoTag()) {
|
||||||
|
cur = undefined;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTransparentTag(cur)) {
|
||||||
|
cur = cur.parentPath;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findAttributeTags(path, attributeTags = []) {
|
||||||
|
path.get("body.body").forEach(child => {
|
||||||
|
if (isAttributeTag(child)) {
|
||||||
|
attributeTags.push(child);
|
||||||
|
} else if (isTransparentTag(child)) {
|
||||||
|
findAttributeTags(child, attributeTags);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return attributeTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getArgOrSequence(path) {
|
||||||
|
const {
|
||||||
|
node: { arguments: args }
|
||||||
|
} = path;
|
||||||
|
const len = args && args.length;
|
||||||
|
|
||||||
|
if (len) {
|
||||||
|
if (len > 1) {
|
||||||
|
return t.sequenceExpression(args);
|
||||||
|
} else {
|
||||||
|
return args[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
packages/babel-utils/src/template-string.js
Normal file
56
packages/babel-utils/src/template-string.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import jsesc from "jsesc";
|
||||||
|
import { types as t } from "@marko/babel-types";
|
||||||
|
|
||||||
|
export function normalizeTemplateString(quasis, ...expressions) {
|
||||||
|
quasis = quasis.map(q => (t.isTemplateElement(q) ? q.value.cooked : q));
|
||||||
|
|
||||||
|
for (let i = expressions.length; i--; ) {
|
||||||
|
let v = expressions[i];
|
||||||
|
if (t.isTemplateLiteral(v)) {
|
||||||
|
quasis[i] += v.quasis[0].value.cooked;
|
||||||
|
quasis[i + 1] =
|
||||||
|
v.quasis[v.quasis.length - 1].value.cooked + (quasis[i + 1] || "");
|
||||||
|
quasis.splice(
|
||||||
|
i + 1,
|
||||||
|
0,
|
||||||
|
...v.quasis.slice(1, -1).map(q => q.value.cooked)
|
||||||
|
);
|
||||||
|
expressions.splice(i, 1, ...v.expressions);
|
||||||
|
i += v.expressions.length;
|
||||||
|
} else if (t.isStringLiteral(v) || typeof v === "string") {
|
||||||
|
const value = t.isStringLiteral(v) ? v.value : v;
|
||||||
|
quasis[i] += value + quasis[i + 1];
|
||||||
|
expressions.splice(i, 1);
|
||||||
|
quasis.splice(i + 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!expressions.length) {
|
||||||
|
// No expression, just return a literal or empty.
|
||||||
|
const literal = quasis.join("");
|
||||||
|
return literal === "" ? undefined : t.stringLiteral(literal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
expressions.length === 1 &&
|
||||||
|
quasis.length === 2 &&
|
||||||
|
quasis.every(isEmptyString)
|
||||||
|
) {
|
||||||
|
// Only expression `${expr}` just return the expr.
|
||||||
|
return expressions[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do it.
|
||||||
|
return t.templateLiteral(quasis.map(getTemplateElement), expressions);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTemplateElement(s = "") {
|
||||||
|
return t.templateElement({
|
||||||
|
cooked: s,
|
||||||
|
raw: jsesc(s, { quotes: "backtick" })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEmptyString(s = "") {
|
||||||
|
return s === "";
|
||||||
|
}
|
||||||
51
packages/compiler/package.json
Normal file
51
packages/compiler/package.json
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"name": "@marko/compiler",
|
||||||
|
"description": "Marko template to JS compiler.",
|
||||||
|
"version": "5.0.0",
|
||||||
|
"author": "Dylan Piercey <dpiercey@ebay.com>",
|
||||||
|
"bugs": "https://github.com/marko-js/marko/issues/new?template=Bug_report.md",
|
||||||
|
"dependencies": {
|
||||||
|
"@marko/babel-types": "^5.0.0",
|
||||||
|
"@marko/babel-utils": "^5.0.0",
|
||||||
|
"@marko/translator-default": "^5.0.0",
|
||||||
|
"@babel/core": "^7.7.7",
|
||||||
|
"@babel/code-frame": "^7.5.5",
|
||||||
|
"@babel/runtime": "^7.7.7",
|
||||||
|
"@babel/traverse": "^7.7.4",
|
||||||
|
"@babel/types": "^7.7.4",
|
||||||
|
"@babel/parser": "^7.7.7",
|
||||||
|
"@babel/generator": "^7.7.7",
|
||||||
|
"complain": "^1.6.0",
|
||||||
|
"he": "^1.1.0",
|
||||||
|
"htmljs-parser": "^2.7.1",
|
||||||
|
"jsesc": "^2.5.2",
|
||||||
|
"lasso-modules-client": "^2.0.4",
|
||||||
|
"marko": "^5.0.0",
|
||||||
|
"resolve-from": "^5.0.0",
|
||||||
|
"self-closing-tags": "^1.0.1",
|
||||||
|
"strip-ansi": "^5.2.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/marko-js/marko/blob/master/packages/compiler/README.md",
|
||||||
|
"keywords": [
|
||||||
|
"htmljs",
|
||||||
|
"parser",
|
||||||
|
"babel",
|
||||||
|
"plugin",
|
||||||
|
"parse",
|
||||||
|
"marko"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "src/index.js",
|
||||||
|
"main:dev": "src/index.js",
|
||||||
|
"main:npm": "dist/index.js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/marko-js/marko/tree/master/packages/compiler"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
}
|
||||||
|
}
|
||||||
172
packages/compiler/src/babel-plugin/hub.js
Normal file
172
packages/compiler/src/babel-plugin/hub.js
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
import path from "path";
|
||||||
|
import { types as t } from "@marko/babel-types";
|
||||||
|
import { getClientPath } from "lasso-modules-client/transport";
|
||||||
|
import { parse, parseExpression } from "@babel/parser";
|
||||||
|
import createFile from "./util/create-file";
|
||||||
|
import codeFrameError from "./util/code-frame-error";
|
||||||
|
import codeFrameWarning from "./util/code-frame-warning";
|
||||||
|
import { getLocRange } from "./util/get-loc";
|
||||||
|
import checksum from "./util/checksum";
|
||||||
|
export class Hub {
|
||||||
|
constructor(filename, code, options) {
|
||||||
|
this._code = code;
|
||||||
|
this.options = options;
|
||||||
|
this.filename = filename;
|
||||||
|
this.file = createFile(filename, code);
|
||||||
|
this.lookup = this.options.lookup;
|
||||||
|
this.macros = Object.create(null);
|
||||||
|
this.meta = this.file.markoMeta = {
|
||||||
|
id: checksum(this.getClientPath(this.filename)),
|
||||||
|
deps: [],
|
||||||
|
tags: []
|
||||||
|
};
|
||||||
|
this._imports = Object.create(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCode() {
|
||||||
|
return this._code;
|
||||||
|
}
|
||||||
|
|
||||||
|
getWhitespaceBefore(pos) {
|
||||||
|
return (
|
||||||
|
this._codeAsWhitespace ||
|
||||||
|
(this._codeAsWhitespace = this._code.replace(/[^\n\r ]/g, " "))
|
||||||
|
).slice(0, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
buildError(node, msg) {
|
||||||
|
return codeFrameError(this.filename, this._code, msg, node.start, node.end);
|
||||||
|
}
|
||||||
|
|
||||||
|
buildWarning(node, msg) {
|
||||||
|
return codeFrameWarning(this.filename, this._code, msg, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
getClientPath(file) {
|
||||||
|
return getClientPath(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveRelativePath(filename) {
|
||||||
|
const dir = path.dirname(this.filename);
|
||||||
|
let relativePath = path.isAbsolute(filename)
|
||||||
|
? path.relative(dir, filename)
|
||||||
|
: filename;
|
||||||
|
if (/^[^./]/.test(relativePath)) relativePath = `./${relativePath}`;
|
||||||
|
return relativePath.replace(/^(?:\.{1,2}\/)+node_modules\//, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
importDefault(path, file, nameHint) {
|
||||||
|
file = remapProductionMarkoBuild(path, file);
|
||||||
|
const { _imports } = this;
|
||||||
|
let importDeclaration = _imports[file];
|
||||||
|
|
||||||
|
if (!importDeclaration) {
|
||||||
|
importDeclaration = _imports[file] = this.program.pushContainer(
|
||||||
|
"body",
|
||||||
|
t.importDeclaration([], t.stringLiteral(file))
|
||||||
|
)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nameHint) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const specifiers = importDeclaration.get("specifiers");
|
||||||
|
const specifier = specifiers.find(specifier =>
|
||||||
|
specifier.isImportDefaultSpecifier()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!specifier) {
|
||||||
|
const identifier = path.scope.generateUidIdentifier(nameHint);
|
||||||
|
importDeclaration.pushContainer(
|
||||||
|
"specifiers",
|
||||||
|
t.importDefaultSpecifier(identifier)
|
||||||
|
);
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.identifier(specifier.node.local.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
importNamed(path, file, name, nameHint = name) {
|
||||||
|
file = remapProductionMarkoBuild(path, file);
|
||||||
|
const { _imports } = this;
|
||||||
|
let importDeclaration = _imports[file];
|
||||||
|
|
||||||
|
if (!importDeclaration) {
|
||||||
|
importDeclaration = _imports[file] = this.program.pushContainer(
|
||||||
|
"body",
|
||||||
|
t.importDeclaration([], t.stringLiteral(file))
|
||||||
|
)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const specifiers = importDeclaration.get("specifiers");
|
||||||
|
const specifier = specifiers.find(
|
||||||
|
specifier =>
|
||||||
|
specifier.isImportSpecifier() && specifier.node.imported.name === name
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!specifier) {
|
||||||
|
const identifier = path.scope.generateUidIdentifier(nameHint);
|
||||||
|
importDeclaration.pushContainer(
|
||||||
|
"specifiers",
|
||||||
|
t.importSpecifier(identifier, t.identifier(name))
|
||||||
|
);
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.identifier(specifier.node.local.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
addStaticNode(node) {
|
||||||
|
this.program.pushContainer("body", node);
|
||||||
|
}
|
||||||
|
|
||||||
|
createNode(type, start, end, ...args) {
|
||||||
|
return {
|
||||||
|
...t[type](...args),
|
||||||
|
...getLocRange(this._code, start, end)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
parse(str, start) {
|
||||||
|
return this._tryParseJS(false, str, start);
|
||||||
|
}
|
||||||
|
|
||||||
|
parseExpression(str, start) {
|
||||||
|
return this._tryParseJS(true, str, start);
|
||||||
|
}
|
||||||
|
|
||||||
|
_tryParseJS(isExpression, str, start) {
|
||||||
|
const opts = this.options.jsParseOptions;
|
||||||
|
str = this.getWhitespaceBefore(start) + str;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return isExpression
|
||||||
|
? parseExpression(str, opts)
|
||||||
|
: parse(str, opts).program;
|
||||||
|
} catch (err) {
|
||||||
|
let { pos, message } = err;
|
||||||
|
if (pos) {
|
||||||
|
throw codeFrameError(
|
||||||
|
this.filename,
|
||||||
|
this._code,
|
||||||
|
message.replace(/ *\(\d+:\d+\)$/, ""),
|
||||||
|
pos
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function remapProductionMarkoBuild(path, file) {
|
||||||
|
const {
|
||||||
|
hub: {
|
||||||
|
options: { isProduction }
|
||||||
|
}
|
||||||
|
} = path;
|
||||||
|
if (!isProduction) return file;
|
||||||
|
return file.replace(/^marko\/src\//, "marko/dist/");
|
||||||
|
}
|
||||||
77
packages/compiler/src/babel-plugin/index.js
Normal file
77
packages/compiler/src/babel-plugin/index.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import { extname, dirname } from "path";
|
||||||
|
import { Hub } from "./hub";
|
||||||
|
import { parse } from "./parser";
|
||||||
|
import { visitor as migrate } from "./plugins/migrate";
|
||||||
|
import { visitor as transform } from "./plugins/transform";
|
||||||
|
import { NodePath, visitors } from "@babel/traverse";
|
||||||
|
import { buildLookup } from "../taglib";
|
||||||
|
|
||||||
|
export default (api, options) => {
|
||||||
|
api.assertVersion(7);
|
||||||
|
options.output = options.output || "html";
|
||||||
|
options.translator = options.translator || "default";
|
||||||
|
|
||||||
|
const isProduction = api.env("production");
|
||||||
|
const translator = require(`@marko/translator-${options.translator}`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: "marko",
|
||||||
|
parserOverride(code, jsParseOptions) {
|
||||||
|
const filename = jsParseOptions.sourceFileName;
|
||||||
|
const hub = new Hub(filename, code, {
|
||||||
|
...options,
|
||||||
|
jsParseOptions,
|
||||||
|
isProduction,
|
||||||
|
lookup: buildLookup(dirname(filename), translator.taglibs)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only run on Marko files.
|
||||||
|
if (!(extname(filename) === ".marko" || options.allExtensions)) {
|
||||||
|
return hub.parse(code, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodePath = new NodePath(hub);
|
||||||
|
nodePath.node = hub.file;
|
||||||
|
hub.program = nodePath.get("program");
|
||||||
|
parse(nodePath);
|
||||||
|
|
||||||
|
// TODO: this package should be split into 4:
|
||||||
|
// 1. babel-syntax-marko (removes the need for the _parseOnly option)
|
||||||
|
// 2. babel-plugin-migrate-marko (removes the need for the _migrateOnly option)
|
||||||
|
// 3. babel-plugin-transform-marko (only runs transformers without converting Marko nodes to js)
|
||||||
|
// 4. babel-plugin-translate-marko (runs final translations)
|
||||||
|
if (!options._parseOnly) {
|
||||||
|
nodePath.get("program").scope.crawl(); // Initialize bindings.
|
||||||
|
const rootMigrators = Object.values(hub.lookup.taglibsById)
|
||||||
|
.map(taglib => taglib.getMigrator())
|
||||||
|
.filter(m => m)
|
||||||
|
.map(m => m.default || m)
|
||||||
|
.map(m => m(api, options));
|
||||||
|
nodePath.traverse(
|
||||||
|
rootMigrators.length
|
||||||
|
? visitors.merge(rootMigrators.concat(migrate))
|
||||||
|
: migrate
|
||||||
|
);
|
||||||
|
if (!options._migrateOnly) {
|
||||||
|
const rootTransformers = hub.lookup.merged.transformers
|
||||||
|
.map(t => require(t.path))
|
||||||
|
.map(t => t.default || t)
|
||||||
|
.map(t => t(api, options));
|
||||||
|
nodePath.traverse(
|
||||||
|
rootTransformers.length
|
||||||
|
? visitors.merge(rootTransformers.concat(transform))
|
||||||
|
: transform
|
||||||
|
);
|
||||||
|
|
||||||
|
nodePath.traverse(translator.visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.assign({}, hub.file);
|
||||||
|
},
|
||||||
|
post(file) {
|
||||||
|
// Attach marko metadata to babel metadata.
|
||||||
|
file.metadata.marko = file.ast.markoMeta;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
322
packages/compiler/src/babel-plugin/parser.js
Normal file
322
packages/compiler/src/babel-plugin/parser.js
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
import { createParser } from "htmljs-parser";
|
||||||
|
import parseAttributes from "./util/parse-attributes";
|
||||||
|
import parseArguments from "./util/parse-arguments";
|
||||||
|
import parseParams from "./util/parse-params";
|
||||||
|
import parseIDShorthand from "./util/parse-id-shorthand";
|
||||||
|
import parseClassnameShorthand from "./util/parse-classname-shorthand";
|
||||||
|
import { getLocRange } from "./util/get-loc";
|
||||||
|
import { types as t } from "@marko/babel-types";
|
||||||
|
|
||||||
|
const EMPTY_OBJECT = {};
|
||||||
|
const EMPTY_ARRAY = [];
|
||||||
|
const htmlTrimStart = t => t.replace(/^[\n\r]\s*/, "");
|
||||||
|
const htmlTrimEnd = t => t.replace(/[\n\r]\s*$/, "");
|
||||||
|
const htmlTrim = t => htmlTrimStart(htmlTrimEnd(t));
|
||||||
|
const isAttributeTag = node =>
|
||||||
|
t.isStringLiteral(node.name) && node.name.value[0] === "@";
|
||||||
|
|
||||||
|
export function parse(fileNodePath) {
|
||||||
|
const { hub } = fileNodePath;
|
||||||
|
const { filename, htmlParseOptions = {} } = hub;
|
||||||
|
const { preserveWhitespace } = htmlParseOptions;
|
||||||
|
const code = hub.getCode();
|
||||||
|
const getTagBody = () =>
|
||||||
|
currentTag.get(currentTag.isFile() ? "program" : "body");
|
||||||
|
const pushTagBody = node => getTagBody().pushContainer("body", node);
|
||||||
|
let currentTag = fileNodePath;
|
||||||
|
let preservingWhitespaceUntil = preserveWhitespace;
|
||||||
|
let wasSelfClosing = false;
|
||||||
|
let handledTagName = false;
|
||||||
|
let onNext;
|
||||||
|
|
||||||
|
const handlers = {
|
||||||
|
onDocumentType({ value, pos, endPos }) {
|
||||||
|
const node = hub.createNode("markoDocumentType", pos, endPos, value);
|
||||||
|
pushTagBody(node);
|
||||||
|
/* istanbul ignore next */
|
||||||
|
onNext = onNext && onNext(node);
|
||||||
|
},
|
||||||
|
|
||||||
|
onDeclaration({ value, pos, endPos }) {
|
||||||
|
const node = hub.createNode("markoDeclaration", pos, endPos, value);
|
||||||
|
pushTagBody(node);
|
||||||
|
/* istanbul ignore next */
|
||||||
|
onNext = onNext && onNext(node);
|
||||||
|
},
|
||||||
|
|
||||||
|
onComment({ value, pos, endPos }) {
|
||||||
|
const node = hub.createNode("markoComment", pos, endPos, value);
|
||||||
|
pushTagBody(node);
|
||||||
|
onNext = onNext && onNext(node);
|
||||||
|
},
|
||||||
|
|
||||||
|
onCDATA({ value, pos, endPos }) {
|
||||||
|
const node = hub.createNode("markoCDATA", pos, endPos, value);
|
||||||
|
pushTagBody(node);
|
||||||
|
onNext = onNext && onNext(node);
|
||||||
|
},
|
||||||
|
|
||||||
|
onText({ value }, { pos }) {
|
||||||
|
const shouldTrim = !preservingWhitespaceUntil;
|
||||||
|
const { body } = getTagBody().node;
|
||||||
|
|
||||||
|
if (shouldTrim) {
|
||||||
|
if (htmlTrim(value) === "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find previous non-scriptlet/@tag.
|
||||||
|
let prev;
|
||||||
|
let prevIndex = body.length;
|
||||||
|
while (prevIndex > 0) {
|
||||||
|
prev = body[--prevIndex];
|
||||||
|
|
||||||
|
if (
|
||||||
|
t.isMarkoClass(prev) ||
|
||||||
|
t.isMarkoComment(prev) ||
|
||||||
|
t.isMarkoScriptlet(prev) ||
|
||||||
|
isAttributeTag(prev)
|
||||||
|
) {
|
||||||
|
prev = undefined;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!prev) {
|
||||||
|
const originalValue = value;
|
||||||
|
value = htmlTrimStart(value);
|
||||||
|
pos += originalValue.indexOf(value);
|
||||||
|
} else if (
|
||||||
|
t.isMarkoText(prev) &&
|
||||||
|
/\s/.test(prev.value[prev.value.length - 1])
|
||||||
|
) {
|
||||||
|
const originalValue = value;
|
||||||
|
value = value.replace(/^\s+/, "");
|
||||||
|
pos += originalValue.indexOf(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const endPos = pos + value.length;
|
||||||
|
const node = hub.createNode("markoText", pos, endPos, value);
|
||||||
|
const prevBody = getTagBody().node.body;
|
||||||
|
pushTagBody(node);
|
||||||
|
onNext && onNext(node);
|
||||||
|
onNext =
|
||||||
|
shouldTrim &&
|
||||||
|
(next => {
|
||||||
|
if (!next || prevBody.indexOf(next) === -1) {
|
||||||
|
node.value = htmlTrimEnd(node.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.value = node.value.replace(/\s+/g, " ");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onPlaceholder({ escape, value, withinBody, pos, endPos }) {
|
||||||
|
if (withinBody) {
|
||||||
|
const node = hub.createNode(
|
||||||
|
"markoPlaceholder",
|
||||||
|
pos,
|
||||||
|
endPos,
|
||||||
|
hub.parseExpression(value, pos + (escape ? 2 /* ${ */ : 3) /* $!{ */),
|
||||||
|
escape
|
||||||
|
);
|
||||||
|
|
||||||
|
pushTagBody(node);
|
||||||
|
onNext = onNext && onNext(node);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onScriptlet({ value, line, block, pos, endPos }) {
|
||||||
|
if (!line && !block) {
|
||||||
|
throw hub.buildError(
|
||||||
|
{ start: pos, end: endPos },
|
||||||
|
"<% scriptlets %> are no longer supported."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pos -= 1; // Include $.
|
||||||
|
// Scriptlets are ignored as content and don't call `onNext`.
|
||||||
|
pushTagBody(
|
||||||
|
hub.createNode(
|
||||||
|
"markoScriptlet",
|
||||||
|
pos,
|
||||||
|
endPos,
|
||||||
|
hub.parse(value, pos + 2 /** Ignores leading `$ ` */).body
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
onOpenTagName(event) {
|
||||||
|
const { pos, endPos } = event;
|
||||||
|
const tagName = event.tagName || "div";
|
||||||
|
const [, tagNameExpression] =
|
||||||
|
/^\$\{([\s\S]*)\}/.exec(tagName) || EMPTY_ARRAY;
|
||||||
|
const tagDef = !tagNameExpression && hub.lookup.getTag(tagName);
|
||||||
|
const tagNameStartPos = pos + (event.concise ? 0 : 1); // Account for leading `<`.
|
||||||
|
|
||||||
|
handledTagName = true;
|
||||||
|
|
||||||
|
if (tagNameExpression === "") {
|
||||||
|
throw hub.buildError(
|
||||||
|
{ start: tagNameStartPos + 1, end: tagNameStartPos + 3 },
|
||||||
|
"Missing expression for <${dynamic}> tag."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const node = hub.createNode(
|
||||||
|
"markoTag",
|
||||||
|
pos,
|
||||||
|
endPos,
|
||||||
|
tagNameExpression
|
||||||
|
? hub.parseExpression(tagNameExpression, tagNameStartPos + 2 /* ${ */)
|
||||||
|
: hub.createNode(
|
||||||
|
"stringLiteral",
|
||||||
|
tagNameStartPos,
|
||||||
|
tagNameStartPos + tagName.length,
|
||||||
|
tagName
|
||||||
|
),
|
||||||
|
[],
|
||||||
|
t.markoTagBody()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tagDef) {
|
||||||
|
node.tagDef = tagDef;
|
||||||
|
|
||||||
|
const { parseOptions } = tagDef;
|
||||||
|
if (parseOptions) {
|
||||||
|
event.setParseOptions(parseOptions);
|
||||||
|
|
||||||
|
if (parseOptions.rootOnly && !currentTag.isFile()) {
|
||||||
|
throw hub.buildError(
|
||||||
|
{ start: pos, end: endPos },
|
||||||
|
`"${tagName}" tags must be at the root of your Marko template.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[currentTag] = pushTagBody(node);
|
||||||
|
|
||||||
|
// @tags are not treated as content and do not call next.
|
||||||
|
if (!isAttributeTag(node)) {
|
||||||
|
onNext = onNext && onNext(node);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onOpenTag(event, parser) {
|
||||||
|
if (!handledTagName) {
|
||||||
|
// There is a bug in htmljs parser where a single top level concise mode tag with nothing else
|
||||||
|
// does not emit the openTagNameEvent.
|
||||||
|
handlers.onOpenTagName(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
handledTagName = false;
|
||||||
|
const { pos, endPos, tagNameEndPos } = event;
|
||||||
|
const { tagDef } = currentTag.node;
|
||||||
|
const parseOptions = (tagDef && tagDef.parseOptions) || EMPTY_OBJECT;
|
||||||
|
wasSelfClosing = event.selfClosed;
|
||||||
|
|
||||||
|
if (parseOptions.state === "parsed-text") {
|
||||||
|
parser.enterParsedTextContentState();
|
||||||
|
} else if (parseOptions.state === "static-text") {
|
||||||
|
parser.enterStaticTextContentState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parseOptions.rawOpenTag) {
|
||||||
|
currentTag.set(
|
||||||
|
"rawValue",
|
||||||
|
parser.substring(pos, endPos).replace(/^<|\/>$|>$/g, "")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parseOptions.ignoreAttributes) {
|
||||||
|
currentTag.set("params", parseParams(hub, event.params));
|
||||||
|
currentTag.set("arguments", parseArguments(hub, event.argument));
|
||||||
|
currentTag.set(
|
||||||
|
"attributes",
|
||||||
|
parseIDShorthand(
|
||||||
|
hub,
|
||||||
|
event.shorthandId,
|
||||||
|
parseClassnameShorthand(
|
||||||
|
hub,
|
||||||
|
event.shorthandClassNames,
|
||||||
|
parseAttributes(hub, event.attributes, tagNameEndPos)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preservingWhitespaceUntil && parseOptions.preserveWhitespace) {
|
||||||
|
preservingWhitespaceUntil = currentTag;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onCloseTag(event, parser) {
|
||||||
|
let { pos, endPos } = event;
|
||||||
|
const tag = currentTag;
|
||||||
|
const { node } = tag;
|
||||||
|
const { tagDef } = node;
|
||||||
|
const isConcise = code[pos] !== "<";
|
||||||
|
|
||||||
|
if (preservingWhitespaceUntil === currentTag) {
|
||||||
|
preservingWhitespaceUntil = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pos) {
|
||||||
|
pos = parser.pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!endPos) {
|
||||||
|
endPos = pos;
|
||||||
|
|
||||||
|
if (wasSelfClosing && !isConcise) {
|
||||||
|
endPos += 2; // account for "/>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(node, getLocRange(code, node.start, endPos));
|
||||||
|
|
||||||
|
if (
|
||||||
|
!isConcise &&
|
||||||
|
!wasSelfClosing &&
|
||||||
|
code[pos + 1] !== "/" &&
|
||||||
|
!currentTag.get("name").isStringLiteral()
|
||||||
|
) {
|
||||||
|
throw hub.buildError(
|
||||||
|
{ start: pos, end: endPos },
|
||||||
|
`Invalid ending for dynamic tag, expected "</>".`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagDef && tagDef.nodeFactoryPath) {
|
||||||
|
const module = require(tagDef.nodeFactoryPath);
|
||||||
|
/* istanbul ignore next */
|
||||||
|
const { default: fn = module } = module;
|
||||||
|
fn(tag, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTag = currentTag.parentPath.parentPath;
|
||||||
|
},
|
||||||
|
|
||||||
|
onfinish() {
|
||||||
|
onNext = onNext && onNext();
|
||||||
|
},
|
||||||
|
|
||||||
|
onError({ message, pos, endPos }) {
|
||||||
|
if (message.includes("EOF")) endPos = pos;
|
||||||
|
throw hub.buildError({ start: pos, end: endPos }, message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
createParser(handlers, {
|
||||||
|
isOpenTagOnly(name) {
|
||||||
|
const { parseOptions = EMPTY_OBJECT } =
|
||||||
|
hub.lookup.getTag(name) || EMPTY_OBJECT;
|
||||||
|
return parseOptions.openTagOnly;
|
||||||
|
},
|
||||||
|
ignoreNonstandardStringPlaceholders: true,
|
||||||
|
...htmlParseOptions
|
||||||
|
}).parse(code, filename);
|
||||||
|
}
|
||||||
47
packages/compiler/src/babel-plugin/plugins/migrate.js
Normal file
47
packages/compiler/src/babel-plugin/plugins/migrate.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { types as t } from "@marko/babel-types";
|
||||||
|
import { getTagDef } from "@marko/babel-utils";
|
||||||
|
import { enter, exit } from "../util/plugin-hooks";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies custom migrators on tags.
|
||||||
|
*/
|
||||||
|
export const visitor = {
|
||||||
|
MarkoTag: {
|
||||||
|
enter(path) {
|
||||||
|
const migrators = getMigratorsForTag(path);
|
||||||
|
const { node } = path;
|
||||||
|
for (const migrator of migrators) {
|
||||||
|
enter(migrator, path, t);
|
||||||
|
if (path.node !== node) break; // Stop if node is replaced.
|
||||||
|
}
|
||||||
|
},
|
||||||
|
exit(path) {
|
||||||
|
const migrators = getMigratorsForTag(path);
|
||||||
|
const { node } = path;
|
||||||
|
for (const migrator of migrators) {
|
||||||
|
exit(migrator, path, t);
|
||||||
|
if (path.node !== node) break; // Stop if node is replaced.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function getMigratorsForTag(path) {
|
||||||
|
const { hub } = path;
|
||||||
|
const { lookup } = hub;
|
||||||
|
const tagName = path.get("name.value").node;
|
||||||
|
const MIGRATOR_CACHE = (lookup.MIGRATOR_CACHE = lookup.MIGRATOR_CACHE || {});
|
||||||
|
|
||||||
|
let migrators = MIGRATOR_CACHE[tagName];
|
||||||
|
|
||||||
|
if (!migrators) {
|
||||||
|
const tagDef = getTagDef(path);
|
||||||
|
|
||||||
|
migrators = MIGRATOR_CACHE[tagName] = [
|
||||||
|
...(tagDef ? tagDef.migratorPaths : []),
|
||||||
|
...(lookup.getTag("*") || { migratorPaths: [] }).migratorPaths
|
||||||
|
].map(path => require(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
return migrators;
|
||||||
|
}
|
||||||
67
packages/compiler/src/babel-plugin/plugins/transform.js
Normal file
67
packages/compiler/src/babel-plugin/plugins/transform.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { types as t } from "@marko/babel-types";
|
||||||
|
import { getTagDef } from "@marko/babel-utils";
|
||||||
|
import { enter, exit } from "../util/plugin-hooks";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies custom transformers on tags.
|
||||||
|
*/
|
||||||
|
export const visitor = {
|
||||||
|
Program(path) {
|
||||||
|
path.hub._componentDefIdentifier = path.scope.generateUidIdentifier(
|
||||||
|
"component"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
MarkoTag: {
|
||||||
|
enter(path) {
|
||||||
|
const transformers = getTransformersForTag(path);
|
||||||
|
const { node } = path;
|
||||||
|
|
||||||
|
for (const transformer of transformers) {
|
||||||
|
enter(transformer, path, t);
|
||||||
|
if (path.node !== node) break; // Stop if node is replaced.
|
||||||
|
}
|
||||||
|
},
|
||||||
|
exit(path) {
|
||||||
|
const transformers = getTransformersForTag(path);
|
||||||
|
const { node } = path;
|
||||||
|
|
||||||
|
for (const transformer of transformers) {
|
||||||
|
exit(transformer, path, t);
|
||||||
|
if (path.node !== node) break; // Stop if node is replaced.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function getTransformersForTag(path) {
|
||||||
|
const { hub } = path;
|
||||||
|
const { lookup } = hub;
|
||||||
|
const tagName = path.get("name.value").node || "*";
|
||||||
|
const TRANSFORMER_CACHE = (lookup.TRANSFORMER_CACHE =
|
||||||
|
lookup.TRANSFORMER_CACHE || {});
|
||||||
|
|
||||||
|
let transformers = TRANSFORMER_CACHE[tagName];
|
||||||
|
|
||||||
|
if (!transformers) {
|
||||||
|
const tagDef = getTagDef(path);
|
||||||
|
|
||||||
|
transformers = TRANSFORMER_CACHE[tagName] = (tagDef
|
||||||
|
? Object.values(tagDef.transformers)
|
||||||
|
: []
|
||||||
|
)
|
||||||
|
.concat(
|
||||||
|
Object.values((lookup.getTag("*") || { transformers: [] }).transformers)
|
||||||
|
)
|
||||||
|
.sort(comparePriority)
|
||||||
|
.map(({ path }) => require(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
return transformers;
|
||||||
|
}
|
||||||
|
|
||||||
|
function comparePriority(a, b) {
|
||||||
|
a = a.priority || 0;
|
||||||
|
b = b.priority || 0;
|
||||||
|
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
11
packages/compiler/src/babel-plugin/util/checksum.js
Normal file
11
packages/compiler/src/babel-plugin/util/checksum.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { createHash } from "crypto";
|
||||||
|
|
||||||
|
export default function checksum(filename) {
|
||||||
|
const hash = createHash("sha1");
|
||||||
|
hash.update(filename);
|
||||||
|
return hash
|
||||||
|
.digest("base64")
|
||||||
|
.slice(0, 8)
|
||||||
|
.replace(/\//g, "-")
|
||||||
|
.replace(/\+/g, "_");
|
||||||
|
}
|
||||||
14
packages/compiler/src/babel-plugin/util/code-frame-error.js
Normal file
14
packages/compiler/src/babel-plugin/util/code-frame-error.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { relative } from "path";
|
||||||
|
import { codeFrameColumns } from "@babel/code-frame";
|
||||||
|
import { getLoc } from "./get-loc";
|
||||||
|
const cwd = process.cwd();
|
||||||
|
|
||||||
|
export default (filename = "", code, msg, startPos, endPos) => {
|
||||||
|
const start = getLoc(code, startPos);
|
||||||
|
const end = endPos != null && getLoc(code, endPos);
|
||||||
|
const frame = codeFrameColumns(code, { start, end }, { highlightCode: true });
|
||||||
|
const position = `(${start.line},${start.column})`;
|
||||||
|
return new SyntaxError(
|
||||||
|
`${relative(cwd, filename)}${position}: ${msg}\n${frame}`
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { relative } from "path";
|
||||||
|
import { codeFrameColumns } from "@babel/code-frame";
|
||||||
|
import { getLoc } from "./get-loc";
|
||||||
|
import complain from "complain";
|
||||||
|
const cwd = process.cwd();
|
||||||
|
|
||||||
|
export default (filename = "", code, msg, node) => {
|
||||||
|
const start = getLoc(code, node.start);
|
||||||
|
const end = node.end != null && getLoc(code, node.end);
|
||||||
|
const frame = codeFrameColumns(code, { start, end }, { highlightCode: true });
|
||||||
|
const position = `(${start.line},${start.column})`;
|
||||||
|
const location = node && node.pos;
|
||||||
|
const options = { location };
|
||||||
|
|
||||||
|
if (location != null) {
|
||||||
|
options.location = position;
|
||||||
|
} else {
|
||||||
|
options.location = filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
return complain(
|
||||||
|
`${relative(cwd, filename)}${position}: ${msg}\n${frame}`,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
};
|
||||||
21
packages/compiler/src/babel-plugin/util/create-file.js
Normal file
21
packages/compiler/src/babel-plugin/util/create-file.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { getLoc } from "./get-loc";
|
||||||
|
|
||||||
|
export default (filename, code) => {
|
||||||
|
const opts = { filename };
|
||||||
|
const start = { line: 0, column: 0 };
|
||||||
|
const end = getLoc(code, code.length);
|
||||||
|
const loc = { start, end, loc: { start, end } };
|
||||||
|
return {
|
||||||
|
type: "File",
|
||||||
|
code,
|
||||||
|
opts,
|
||||||
|
...loc,
|
||||||
|
program: {
|
||||||
|
type: "Program",
|
||||||
|
sourceType: "module",
|
||||||
|
...loc,
|
||||||
|
body: [],
|
||||||
|
directives: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
28
packages/compiler/src/babel-plugin/util/get-loc.js
Normal file
28
packages/compiler/src/babel-plugin/util/get-loc.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
const NEW_LINE = /\r?\n/g;
|
||||||
|
|
||||||
|
export function getLoc(code, pos) {
|
||||||
|
const src = code.slice(0, pos);
|
||||||
|
let lastIndex = 0;
|
||||||
|
let line = 1;
|
||||||
|
|
||||||
|
while (NEW_LINE.exec(src)) {
|
||||||
|
line++;
|
||||||
|
lastIndex = NEW_LINE.lastIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
line,
|
||||||
|
column: pos - lastIndex + 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLocRange(code, start, end) {
|
||||||
|
return {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
loc: {
|
||||||
|
start: getLoc(code, start),
|
||||||
|
end: getLoc(code, end)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
export default (hub, details) => {
|
||||||
|
if (details) {
|
||||||
|
return hub.parseExpression(`_(${details.value})`, details.pos - 1)
|
||||||
|
.arguments;
|
||||||
|
}
|
||||||
|
};
|
||||||
60
packages/compiler/src/babel-plugin/util/parse-attributes.js
Normal file
60
packages/compiler/src/babel-plugin/util/parse-attributes.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { types as t } from "@marko/babel-types";
|
||||||
|
import parseArguments from "./parse-arguments";
|
||||||
|
|
||||||
|
export default (hub, attributes, startPos) => {
|
||||||
|
const code = hub.getCode();
|
||||||
|
let attrEndPos = startPos;
|
||||||
|
|
||||||
|
return attributes.map(attr => {
|
||||||
|
const attrStartPos = code.indexOf(attr.name, attrEndPos);
|
||||||
|
|
||||||
|
if (attr.name.slice(0, 3) === "...") {
|
||||||
|
let attrExpression = attr.name.slice(3);
|
||||||
|
|
||||||
|
if (attr.argument) {
|
||||||
|
attrExpression += `(${attr.argument.value})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
attrEndPos = attrStartPos + attrExpression.length;
|
||||||
|
|
||||||
|
const value = hub.parseExpression(attrExpression, attrStartPos + 3);
|
||||||
|
|
||||||
|
// TODO: Inline merge object literals.
|
||||||
|
return hub.createNode(
|
||||||
|
"markoSpreadAttribute",
|
||||||
|
attrStartPos,
|
||||||
|
attrEndPos,
|
||||||
|
value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const match = /:(.*)$/.exec(attr.name);
|
||||||
|
const modifier = match && match[1];
|
||||||
|
let name = attr.name;
|
||||||
|
let value;
|
||||||
|
|
||||||
|
if (modifier) {
|
||||||
|
name = name.slice(0, name.length - modifier.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attr.value) {
|
||||||
|
attrEndPos = attr.endPos;
|
||||||
|
const valueStart = attr.pos + 1; // Add one to account for "=".
|
||||||
|
const rawValue = code.slice(valueStart, attrEndPos); // We use the raw value to ignore things like non standard placeholders.
|
||||||
|
value = hub.parseExpression(rawValue, valueStart);
|
||||||
|
} else {
|
||||||
|
attrEndPos = attr.argument ? attr.argument.endPos + 1 : attr.endPos;
|
||||||
|
value = t.booleanLiteral(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hub.createNode(
|
||||||
|
"markoAttribute",
|
||||||
|
attrStartPos,
|
||||||
|
attrEndPos,
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
modifier,
|
||||||
|
parseArguments(hub, attr.argument)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
import { types as t } from "@marko/babel-types";
|
||||||
|
|
||||||
|
export default (hub, shorthands, attributes) => {
|
||||||
|
if (!shorthands) {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
const classAttr = attributes.find(({ name }) => name === "class");
|
||||||
|
const classParts = shorthands.map(({ rawParts }) => {
|
||||||
|
const nodes = rawParts.map(part =>
|
||||||
|
part.expression
|
||||||
|
? hub.parseExpression(part.expression, part.pos)
|
||||||
|
: hub.createNode("stringLiteral", part.pos, part.endPos, part.text)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (nodes.length === 1) {
|
||||||
|
return nodes[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes.reduce((a, b) => t.binaryExpression("+", a, b));
|
||||||
|
});
|
||||||
|
|
||||||
|
let shorthandNode;
|
||||||
|
if (classParts.length === 1) {
|
||||||
|
shorthandNode = classParts[0];
|
||||||
|
} else if (classParts.every(node => t.isStringLiteral(node))) {
|
||||||
|
const combinedStartPos = shorthands[0].rawParts[0].pos;
|
||||||
|
const lastParts = shorthands[shorthands.length - 1].rawParts;
|
||||||
|
const combinedEndPos = lastParts[lastParts.length - 1].endPos;
|
||||||
|
shorthandNode = hub.createNode(
|
||||||
|
"stringLiteral",
|
||||||
|
combinedStartPos,
|
||||||
|
combinedEndPos,
|
||||||
|
classParts.map(node => node.value).join(" ")
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
shorthandNode = t.arrayExpression(classParts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (classAttr) {
|
||||||
|
if (t.isArrayExpression(classAttr.value)) {
|
||||||
|
if (t.isArrayExpression(shorthandNode)) {
|
||||||
|
classAttr.value.elements.push(...shorthandNode.elements);
|
||||||
|
} else {
|
||||||
|
classAttr.value.elements.push(shorthandNode);
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
t.isStringLiteral(classAttr.value) &&
|
||||||
|
t.isStringLiteral(shorthandNode)
|
||||||
|
) {
|
||||||
|
classAttr.value.value = `${shorthandNode.value} ${classAttr.value.value}`;
|
||||||
|
} else if (t.isArrayExpression(shorthandNode)) {
|
||||||
|
shorthandNode.elements.push(classAttr.value);
|
||||||
|
classAttr.value = shorthandNode;
|
||||||
|
} else {
|
||||||
|
classAttr.value = t.arrayExpression([shorthandNode, classAttr.value]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
attributes.push(t.markoAttribute("class", shorthandNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
return attributes;
|
||||||
|
};
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
import { types as t } from "@marko/babel-types";
|
||||||
|
|
||||||
|
export default (hub, shorthand, attributes) => {
|
||||||
|
if (!shorthand) {
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
const idAttr = attributes.find(({ name }) => name === "id");
|
||||||
|
if (idAttr) {
|
||||||
|
throw hub.buildError(idAttr, "Cannot have shorthand id and id attribute.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const idParts = shorthand.rawParts.map(part =>
|
||||||
|
part.expression
|
||||||
|
? hub.parseExpression(part.expression, part.pos)
|
||||||
|
: hub.createNode("stringLiteral", part.pos, part.endPos, part.text)
|
||||||
|
);
|
||||||
|
|
||||||
|
attributes.push(
|
||||||
|
t.markoAttribute(
|
||||||
|
"id",
|
||||||
|
idParts.length === 1
|
||||||
|
? idParts[0]
|
||||||
|
: idParts.reduce((a, b) => t.binaryExpression("+", a, b))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return attributes;
|
||||||
|
};
|
||||||
5
packages/compiler/src/babel-plugin/util/parse-params.js
Normal file
5
packages/compiler/src/babel-plugin/util/parse-params.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export default (hub, details) => {
|
||||||
|
if (details) {
|
||||||
|
return hub.parseExpression(`(${details.value})=>{}`, details.pos).params;
|
||||||
|
}
|
||||||
|
};
|
||||||
22
packages/compiler/src/babel-plugin/util/plugin-hooks.js
Normal file
22
packages/compiler/src/babel-plugin/util/plugin-hooks.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Utilities for executing multi step compiler hooks (migrators & transformers).
|
||||||
|
|
||||||
|
export function enter(plugin, ...args) {
|
||||||
|
const fn =
|
||||||
|
(plugin &&
|
||||||
|
(plugin.enter ||
|
||||||
|
(plugin.default && plugin.default.enter) ||
|
||||||
|
plugin.default)) ||
|
||||||
|
plugin;
|
||||||
|
if (typeof fn === "function") {
|
||||||
|
fn(...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function exit(plugin, ...args) {
|
||||||
|
const fn =
|
||||||
|
plugin &&
|
||||||
|
(plugin.exit || (plugin.default ? plugin.default.exit : undefined));
|
||||||
|
if (typeof fn === "function") {
|
||||||
|
fn(...args);
|
||||||
|
}
|
||||||
|
}
|
||||||
45
packages/compiler/src/config.js
Normal file
45
packages/compiler/src/config.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
let config;
|
||||||
|
const globalThis = typeof window === "undefined" ? global : window;
|
||||||
|
const MARKO_CONFIG_KEY = Symbol("Default Marko Compiler Config");
|
||||||
|
|
||||||
|
if (globalThis[MARKO_CONFIG_KEY]) {
|
||||||
|
config = globalThis[MARKO_CONFIG_KEY];
|
||||||
|
} else {
|
||||||
|
config = globalThis[MARKO_CONFIG_KEY] = {
|
||||||
|
// The default output mode for compiled templates
|
||||||
|
output: "html",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the version should be written to the template as a comment e.g.
|
||||||
|
* // Compiled using marko@4.0.0 - DO NOT EDIT
|
||||||
|
*/
|
||||||
|
writeVersionComment: true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether unrecognized tags should be ignored or not. This flag will
|
||||||
|
* be enabled by default when compiling XML.
|
||||||
|
*/
|
||||||
|
ignoreUnrecognizedTags: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether source maps should be output with the compiled templates.
|
||||||
|
* When `true` a `map` property will be available on the compile result.
|
||||||
|
* When `"inline"` the sourcemap will be inlined as a comment in the output code.
|
||||||
|
* When `"both"` both of the above will be used.
|
||||||
|
*/
|
||||||
|
sourceMaps: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This option inlines all of the meta data in the template.
|
||||||
|
* You can also access this metadata via `compile(...).meta`.
|
||||||
|
* This API is sticking around for compatibility purposes.
|
||||||
|
*/
|
||||||
|
meta: false
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.env.MARKO_CONFIG) {
|
||||||
|
Object.assign(config, JSON.parse(process.env.MARKO_CONFIG));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config;
|
||||||
65
packages/compiler/src/index.js
Normal file
65
packages/compiler/src/index.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import fs from "fs";
|
||||||
|
import { loadPartialConfig, transformAsync, transformSync } from "@babel/core";
|
||||||
|
import corePlugin from "./babel-plugin";
|
||||||
|
import defaultOptions from "./config";
|
||||||
|
import * as taglib from "./taglib";
|
||||||
|
|
||||||
|
export { taglib };
|
||||||
|
|
||||||
|
let globalConfig = Object.assign({}, defaultOptions);
|
||||||
|
export function configure(newConfig = {}) {
|
||||||
|
globalConfig = Object.assign({}, defaultOptions, newConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function compile(src, filename, options) {
|
||||||
|
const babelConfig = loadBabelConfig(filename, options);
|
||||||
|
const babelResult = await transformAsync(src, babelConfig);
|
||||||
|
return buildResult(babelResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compileSync(src, filename, options) {
|
||||||
|
const babelConfig = loadBabelConfig(filename, options);
|
||||||
|
const babelResult = transformSync(src, babelConfig);
|
||||||
|
return buildResult(babelResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function compileFile(filename, options) {
|
||||||
|
const src = await fs.promises.readFile(filename, "utf-8");
|
||||||
|
return compile(src, filename, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compileFileSync(filename, options) {
|
||||||
|
const src = fs.readFileSync(filename, "utf-8");
|
||||||
|
return compileSync(src, filename, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadBabelConfig(filename, options) {
|
||||||
|
const markoConfig = Object.assign({}, globalConfig);
|
||||||
|
|
||||||
|
if (options) {
|
||||||
|
Object.assign(markoConfig, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseBabelConfig = {
|
||||||
|
filename: filename,
|
||||||
|
sourceFileName: filename,
|
||||||
|
sourceType: "module",
|
||||||
|
sourceMaps: markoConfig.sourceMaps,
|
||||||
|
plugins: [[corePlugin, markoConfig]]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (markoConfig.babelConfig) {
|
||||||
|
Object.assign(baseBabelConfig, markoConfig.babelConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
return loadPartialConfig(baseBabelConfig).options;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildResult(babelResult) {
|
||||||
|
const {
|
||||||
|
map,
|
||||||
|
code,
|
||||||
|
metadata: { marko: meta }
|
||||||
|
} = babelResult;
|
||||||
|
return { map, code, meta };
|
||||||
|
}
|
||||||
2294
packages/compiler/src/taglib/html/marko.json
Normal file
2294
packages/compiler/src/taglib/html/marko.json
Normal file
File diff suppressed because it is too large
Load Diff
46
packages/compiler/src/taglib/index.js
Normal file
46
packages/compiler/src/taglib/index.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import path from "path";
|
||||||
|
import loader from "marko/src/taglib/taglib-loader";
|
||||||
|
import finder from "marko/src/taglib/taglib-finder";
|
||||||
|
import TaglibLookup from "marko/src/taglib/taglib-lookup/TaglibLookup";
|
||||||
|
|
||||||
|
export { excludeDir, excludePackage } from "marko/src/taglib/taglib-finder";
|
||||||
|
|
||||||
|
let lookupCache = Object.create(null);
|
||||||
|
const coreTaglibs = ["html", "svg", "math"].map(name =>
|
||||||
|
loader.loadTaglibFromFile(path.join(__dirname, name, "marko.json"))
|
||||||
|
);
|
||||||
|
|
||||||
|
export function buildLookup(dirname, translator = "default") {
|
||||||
|
const translatorTaglibs = Array.isArray(translator)
|
||||||
|
? translator
|
||||||
|
: require(`@marko/translator-${translator}`).taglibs;
|
||||||
|
const taglibsForDir = finder.find(
|
||||||
|
dirname,
|
||||||
|
coreTaglibs.concat(translatorTaglibs)
|
||||||
|
);
|
||||||
|
|
||||||
|
const cacheKey = taglibsForDir.map(it => it.id).join();
|
||||||
|
let lookup = lookupCache[cacheKey];
|
||||||
|
|
||||||
|
if (!lookup) {
|
||||||
|
lookup = lookupCache[cacheKey] = new TaglibLookup();
|
||||||
|
for (const taglib of taglibsForDir) {
|
||||||
|
lookup.addTaglib(taglib);
|
||||||
|
if (taglib.imports) {
|
||||||
|
for (const importedTaglib of taglib.imports) {
|
||||||
|
if (!lookup.hasTaglib(importedTaglib)) {
|
||||||
|
lookup.addTaglib(importedTaglib);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearCaches() {
|
||||||
|
loader.clearCache();
|
||||||
|
finder.clearCache();
|
||||||
|
lookupCache = Object.create(null);
|
||||||
|
}
|
||||||
45
packages/compiler/src/taglib/math/marko.json
Normal file
45
packages/compiler/src/taglib/math/marko.json
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"taglib-id": "marko-math",
|
||||||
|
"<math>": { "html": true, "htmlType": "math" },
|
||||||
|
"<maction>": { "html": true, "htmlType": "math" },
|
||||||
|
"<maligngroup>": { "html": true, "htmlType": "math" },
|
||||||
|
"<malignmark>": { "html": true, "htmlType": "math" },
|
||||||
|
"<menclose>": { "html": true, "htmlType": "math" },
|
||||||
|
"<merror>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mfenced>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mfrac>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mglyph>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mi>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mlabeledtr>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mlongdiv>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mmultiscripts>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mn>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mo>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mover>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mpadded>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mphantom>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mroot>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mrow>": { "html": true, "htmlType": "math" },
|
||||||
|
"<ms>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mscarries>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mscarry>": { "html": true, "htmlType": "math" },
|
||||||
|
"<msgroup>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mstack>": { "html": true, "htmlType": "math" },
|
||||||
|
"<msline>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mspace>": { "html": true, "htmlType": "math" },
|
||||||
|
"<msqrt>": { "html": true, "htmlType": "math" },
|
||||||
|
"<msrow>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mstyle>": { "html": true, "htmlType": "math" },
|
||||||
|
"<msub>": { "html": true, "htmlType": "math" },
|
||||||
|
"<msup>": { "html": true, "htmlType": "math" },
|
||||||
|
"<msubsup>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mtable>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mtd>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mtext>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mtr>": { "html": true, "htmlType": "math" },
|
||||||
|
"<munder>": { "html": true, "htmlType": "math" },
|
||||||
|
"<munderover>": { "html": true, "htmlType": "math" },
|
||||||
|
"<semantics>": { "html": true, "htmlType": "math" },
|
||||||
|
"<mprescripts>": { "html": true, "htmlType": "math" },
|
||||||
|
"<none>": { "html": true, "htmlType": "math" }
|
||||||
|
}
|
||||||
62
packages/compiler/src/taglib/svg/marko.json
Normal file
62
packages/compiler/src/taglib/svg/marko.json
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
"taglib-id": "marko-svg",
|
||||||
|
"<animate>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<animateColor>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<animateMotion>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<animateTransform>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<circle>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<clipPath>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<defs>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<desc>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<ellipse>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feBlend>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feColorMatrix>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feComponentTransfer>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feComposite>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feConvolveMatrix>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feDiffuseLighting>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feDisplacementMap>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feDistantLight>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feFlood>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feFuncA>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feFuncB>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feFuncG>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feFuncR>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feGaussianBlur>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feImage>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feMerge>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feMergeNode>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feMorphology>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feOffset>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<fePointLight>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feSpecularLighting>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feSpotLight>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feTile>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<feTurbulence>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<filter>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<foreignObject>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<g>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<image>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<line>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<linearGradient>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<marker>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<mask>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<metadata>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<mpath>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<path>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<pattern>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<polygon>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<polyline>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<radialGradient>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<rect>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<set>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<stop>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<svg>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<switch>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<symbol>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<text>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<textPath>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<tspan>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<use>": { "html": true, "htmlType": "svg" },
|
||||||
|
"<view>": { "html": true, "htmlType": "svg" }
|
||||||
|
}
|
||||||
63
packages/compiler/test/index.test.js
Normal file
63
packages/compiler/test/index.test.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import autotest from "mocha-autotest";
|
||||||
|
import stripAnsi from "strip-ansi";
|
||||||
|
import { compileFileSync } from "../src";
|
||||||
|
|
||||||
|
fs.readdirSync(path.join(__dirname, "../../"))
|
||||||
|
.map(dir => /^translator-(.*)|/.exec(dir)[1])
|
||||||
|
.filter(Boolean)
|
||||||
|
.forEach(translator => {
|
||||||
|
autotest(path.normalize(`../../translator-${translator}/test/fixtures`), {
|
||||||
|
html: runTest({ output: "html" }),
|
||||||
|
vdom: runTest({ output: "dom" }),
|
||||||
|
generated: runTest({ _parseOnly: true })
|
||||||
|
});
|
||||||
|
|
||||||
|
function runTest(config) {
|
||||||
|
return ({ mode, test, resolve, snapshot }) => {
|
||||||
|
const testConfigFile = resolve("test.js");
|
||||||
|
const testConfig = fs.existsSync(testConfigFile)
|
||||||
|
? require(testConfigFile)
|
||||||
|
: {};
|
||||||
|
const templateFile = resolve(
|
||||||
|
testConfig.templateFile || "template.marko"
|
||||||
|
);
|
||||||
|
|
||||||
|
const compilerConfig = {
|
||||||
|
...config,
|
||||||
|
babelConfig: {
|
||||||
|
babelrc: false,
|
||||||
|
configFile: false
|
||||||
|
},
|
||||||
|
writeVersionComment: false
|
||||||
|
};
|
||||||
|
|
||||||
|
test(() => {
|
||||||
|
let output;
|
||||||
|
try {
|
||||||
|
output = compileFileSync(templateFile, compilerConfig).code;
|
||||||
|
} catch (err) {
|
||||||
|
try {
|
||||||
|
snapshot(stripCwd(stripAnsi(err.message)), {
|
||||||
|
name: `${mode}-error`,
|
||||||
|
ext: ".txt"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} catch {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot(output, {
|
||||||
|
name: mode,
|
||||||
|
ext: mode === "generated" ? ".marko" : ".js"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function stripCwd(message) {
|
||||||
|
return message.replace(process.cwd() + "/", "");
|
||||||
|
}
|
||||||
37
packages/translator-default/package.json
Normal file
37
packages/translator-default/package.json
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"name": "@marko/translator-default",
|
||||||
|
"description": "Translates Marko templates to the default Marko runtime.",
|
||||||
|
"version": "5.0.0",
|
||||||
|
"author": "Dylan Piercey <dpiercey@ebay.com>",
|
||||||
|
"bugs": "https://github.com/marko-js/marko/issues/new?template=Bug_report.md",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.7.7",
|
||||||
|
"@marko/babel-types": "^5.0.0",
|
||||||
|
"@marko/babel-utils": "^5.0.0",
|
||||||
|
"marko": "^5.0.0",
|
||||||
|
"self-closing-tags": "^1.0.1"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/marko-js/marko/blob/master/packages/translator-default/README.md",
|
||||||
|
"keywords": [
|
||||||
|
"htmljs",
|
||||||
|
"parser",
|
||||||
|
"babel",
|
||||||
|
"plugin",
|
||||||
|
"parse",
|
||||||
|
"marko"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "src/index.js",
|
||||||
|
"main:dev": "src/index.js",
|
||||||
|
"main:npm": "dist/index.js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/marko-js/marko/tree/master/packages/translator-default"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
}
|
||||||
|
}
|
||||||
13
packages/translator-default/src/cdata/index.js
Normal file
13
packages/translator-default/src/cdata/index.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import translateHTML from "./index[html]";
|
||||||
|
import translateVDOM from "./index[vdom]";
|
||||||
|
|
||||||
|
export default function(path) {
|
||||||
|
const {
|
||||||
|
hub: { options }
|
||||||
|
} = path;
|
||||||
|
if (options.output === "html") {
|
||||||
|
translateHTML(path);
|
||||||
|
} else {
|
||||||
|
translateVDOM(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
packages/translator-default/src/cdata/index[html].js
Normal file
8
packages/translator-default/src/cdata/index[html].js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { types as t } from "@marko/babel-types";
|
||||||
|
import write from "../util/html-out-write";
|
||||||
|
|
||||||
|
export default function(path) {
|
||||||
|
const { node } = path;
|
||||||
|
|
||||||
|
path.replaceWith(write`<![CDATA[${t.stringLiteral(node.value)}]]>`);
|
||||||
|
}
|
||||||
8
packages/translator-default/src/cdata/index[vdom].js
Normal file
8
packages/translator-default/src/cdata/index[vdom].js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { types as t } from "@marko/babel-types";
|
||||||
|
import write from "../util/vdom-out-write";
|
||||||
|
|
||||||
|
export default function(path) {
|
||||||
|
const { node } = path;
|
||||||
|
|
||||||
|
path.replaceWith(write("t", t.stringLiteral(node.value)));
|
||||||
|
}
|
||||||
60
packages/translator-default/src/class.js
Normal file
60
packages/translator-default/src/class.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { types as t } from "@marko/babel-types";
|
||||||
|
|
||||||
|
export default function(path) {
|
||||||
|
const {
|
||||||
|
hub,
|
||||||
|
node: {
|
||||||
|
body: { body }
|
||||||
|
}
|
||||||
|
} = path;
|
||||||
|
|
||||||
|
const classProperties = [];
|
||||||
|
let onCreateMethod = body.find(
|
||||||
|
prop =>
|
||||||
|
prop.computed === false &&
|
||||||
|
t.isIdentifier(prop.key) &&
|
||||||
|
prop.key.name === "onCreate"
|
||||||
|
);
|
||||||
|
|
||||||
|
const objectProperties = body
|
||||||
|
.map(prop => {
|
||||||
|
if (t.isClassMethod(prop)) {
|
||||||
|
prop.type = "ObjectMethod";
|
||||||
|
delete prop.start;
|
||||||
|
delete prop.end;
|
||||||
|
delete prop.loc;
|
||||||
|
return prop;
|
||||||
|
} else if (t.isClassProperty(prop) && !prop.static) {
|
||||||
|
classProperties.push(
|
||||||
|
t.assignmentExpression(
|
||||||
|
"=",
|
||||||
|
t.memberExpression(t.thisExpression(), prop.key, prop.computed),
|
||||||
|
prop.value
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw hub.buildError(prop, "Unsupported class property on component.");
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
if (classProperties.length) {
|
||||||
|
if (!onCreateMethod) {
|
||||||
|
objectProperties.push(
|
||||||
|
(onCreateMethod = t.objectMethod(
|
||||||
|
"method",
|
||||||
|
t.identifier("onCreate"),
|
||||||
|
[],
|
||||||
|
t.blockStatement([])
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onCreateMethod.body.body.unshift(...classProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
hub.inlineComponentClass = t.objectExpression(objectProperties);
|
||||||
|
path.remove();
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user