mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[ADD] add basic infrastructure to buid owl-runtime without compiler
This commit is contained in:
committed by
Sam Degueldre
parent
b10a700381
commit
e4b810c027
+4
-1
@@ -14,6 +14,8 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:bundle": "rollup -c --failAfterWarnings",
|
"build:bundle": "rollup -c --failAfterWarnings",
|
||||||
|
"build:runtime": "rollup -c --failAfterWarnings runtime",
|
||||||
|
"build:compiler": "rollup -c --failAfterWarnings compiler",
|
||||||
"build": "npm run build:bundle",
|
"build": "npm run build:bundle",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand --watch --testTimeout=5000000",
|
"test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand --watch --testTimeout=5000000",
|
||||||
@@ -25,7 +27,8 @@
|
|||||||
"prettier": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --write",
|
"prettier": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --write",
|
||||||
"check-formatting": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --check",
|
"check-formatting": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md} --check",
|
||||||
"publish": "npm run build && npm publish",
|
"publish": "npm run build && npm publish",
|
||||||
"release": "node tools/release.js"
|
"release": "node tools/release.js",
|
||||||
|
"compile_templates": "node tools/compile_xml.js"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
+36
-28
@@ -3,13 +3,9 @@ import git from "git-rev-sync";
|
|||||||
import typescript from 'rollup-plugin-typescript2';
|
import typescript from 'rollup-plugin-typescript2';
|
||||||
import { terser } from "rollup-plugin-terser";
|
import { terser } from "rollup-plugin-terser";
|
||||||
|
|
||||||
const name = "owl";
|
|
||||||
const extend = true;
|
|
||||||
|
|
||||||
/**
|
let input, output;
|
||||||
* Meta data to be added on the __info__ object.
|
|
||||||
* Used to let external tools know the current owl version.
|
|
||||||
*/
|
|
||||||
const outro = `
|
const outro = `
|
||||||
__info__.version = '${pkg.version}';
|
__info__.version = '${pkg.version}';
|
||||||
__info__.date = '${new Date().toISOString()}';
|
__info__.date = '${new Date().toISOString()}';
|
||||||
@@ -17,13 +13,39 @@ __info__.hash = '${git.short()}';
|
|||||||
__info__.url = 'https://github.com/odoo/owl';
|
__info__.url = 'https://github.com/odoo/owl';
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
switch (process.argv[4]) {
|
||||||
|
case "compiler":
|
||||||
|
input = "src/compiler/index.ts",
|
||||||
|
output = [
|
||||||
|
getConfigForFormat('cjs', 'dist/compiler.js', ''),
|
||||||
|
]
|
||||||
|
break;
|
||||||
|
case "runtime":
|
||||||
|
input = "src/index.runtime.ts";
|
||||||
|
output = [
|
||||||
|
getConfigForFormat('esm', addSuffix(pkg.module, 'runtime'), outro),
|
||||||
|
getConfigForFormat('cjs', addSuffix(pkg.main, 'runtime'), outro),
|
||||||
|
getConfigForFormat('iife', addSuffix(pkg.browser, 'runtime'), outro),
|
||||||
|
getConfigForFormat('iife', addSuffix(pkg.browser, 'runtime'), outro, true),
|
||||||
|
]
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
input = "src/index.ts",
|
||||||
|
output = [
|
||||||
|
getConfigForFormat('esm', pkg.module, outro),
|
||||||
|
getConfigForFormat('cjs', pkg.main, outro),
|
||||||
|
getConfigForFormat('iife', pkg.browser, outro),
|
||||||
|
getConfigForFormat('iife', pkg.browser, outro, true),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate from a string depicting a path a new path for the minified version.
|
* Generate from a string depicting a path a new path for the minified version.
|
||||||
* @param {string} pkgFileName file name
|
* @param {string} pkgFileName file name
|
||||||
*/
|
*/
|
||||||
function generateMinifiedNameFromPkgName(pkgFileName) {
|
function addSuffix(pkgFileName, suffix) {
|
||||||
const parts = pkgFileName.split('.');
|
const parts = pkgFileName.split('.');
|
||||||
parts.splice(parts.length - 1, 0, "min");
|
parts.splice(parts.length - 1, 0, suffix);
|
||||||
return parts.join('.');
|
return parts.join('.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,12 +55,12 @@ function generateMinifiedNameFromPkgName(pkgFileName) {
|
|||||||
* @param {string} generatedFileName generated file name
|
* @param {string} generatedFileName generated file name
|
||||||
* @param {boolean} minified should it be minified
|
* @param {boolean} minified should it be minified
|
||||||
*/
|
*/
|
||||||
function getConfigForFormat(format, generatedFileName, minified = false) {
|
function getConfigForFormat(format, generatedFileName, outro, minified = false) {
|
||||||
return {
|
return {
|
||||||
file: minified ? generateMinifiedNameFromPkgName(generatedFileName) : generatedFileName,
|
file: minified ? addSuffix(generatedFileName, "min") : generatedFileName,
|
||||||
format: format,
|
format: format,
|
||||||
name: name,
|
name: "owl",
|
||||||
extend: extend,
|
extend: true,
|
||||||
outro: outro,
|
outro: outro,
|
||||||
freeze: false,
|
freeze: false,
|
||||||
plugins: minified ? [terser()] : [],
|
plugins: minified ? [terser()] : [],
|
||||||
@@ -47,22 +69,8 @@ function getConfigForFormat(format, generatedFileName, minified = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
input: "src/index.ts",
|
input,
|
||||||
output: [
|
output,
|
||||||
|
|
||||||
/**
|
|
||||||
* Read about module formats:
|
|
||||||
* https://auth0.com/blog/javascript-module-systems-showdown/
|
|
||||||
* https://medium.com/@kelin2025/so-you-wanna-use-es6-modules-714f48b3a953
|
|
||||||
*/
|
|
||||||
|
|
||||||
getConfigForFormat('esm', pkg.module),
|
|
||||||
getConfigForFormat('esm', pkg.module, true),
|
|
||||||
getConfigForFormat('cjs', pkg.main),
|
|
||||||
getConfigForFormat('cjs', pkg.main, true),
|
|
||||||
getConfigForFormat('iife', pkg.browser),
|
|
||||||
getConfigForFormat('iife', pkg.browser, true),
|
|
||||||
],
|
|
||||||
plugins: [
|
plugins: [
|
||||||
typescript({
|
typescript({
|
||||||
useTsconfigDeclarationDir: true
|
useTsconfigDeclarationDir: true
|
||||||
|
|||||||
@@ -118,13 +118,8 @@ export class TemplateSet {
|
|||||||
return this.templates[name];
|
return this.templates[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
_compileTemplate(name: string, template: string | Element) {
|
_compileTemplate(name: string, template: string | Element): ReturnType<typeof compile> {
|
||||||
return compile(template, {
|
throw new Error(`Unable to compile a template. Please use owl full build instead`);
|
||||||
name,
|
|
||||||
dev: this.dev,
|
|
||||||
translateFn: this.translateFn,
|
|
||||||
translatableAttributes: this.translatableAttributes,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callTemplate(owner: any, subTemplate: string, ctx: any, parent: any, key: any): any {
|
callTemplate(owner: any, subTemplate: string, ctx: any, parent: any, key: any): any {
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import {
|
||||||
|
config,
|
||||||
|
createBlock,
|
||||||
|
html,
|
||||||
|
list,
|
||||||
|
mount as blockMount,
|
||||||
|
multi,
|
||||||
|
patch,
|
||||||
|
remove,
|
||||||
|
text,
|
||||||
|
toggler,
|
||||||
|
comment,
|
||||||
|
} from "./blockdom";
|
||||||
|
import { mainEventHandler } from "./component/handler";
|
||||||
|
export type { Reactive } from "./reactivity";
|
||||||
|
|
||||||
|
config.shouldNormalizeDom = false;
|
||||||
|
config.mainEventHandler = mainEventHandler;
|
||||||
|
|
||||||
|
export const blockDom = {
|
||||||
|
config,
|
||||||
|
// bdom entry points
|
||||||
|
mount: blockMount,
|
||||||
|
patch,
|
||||||
|
remove,
|
||||||
|
// bdom block types
|
||||||
|
list,
|
||||||
|
multi,
|
||||||
|
text,
|
||||||
|
toggler,
|
||||||
|
createBlock,
|
||||||
|
html,
|
||||||
|
comment,
|
||||||
|
};
|
||||||
|
|
||||||
|
export { App, mount } from "./app/app";
|
||||||
|
export { xml } from "./app/template_set";
|
||||||
|
export { Component } from "./component/component";
|
||||||
|
export { useComponent, useState } from "./component/component_node";
|
||||||
|
export { status } from "./component/status";
|
||||||
|
export { reactive, markRaw, toRaw } from "./reactivity";
|
||||||
|
export { useEffect, useEnv, useExternalListener, useRef, useChildSubEnv, useSubEnv } from "./hooks";
|
||||||
|
export { EventBus, whenReady, loadFile, markup } from "./utils";
|
||||||
|
export {
|
||||||
|
onWillStart,
|
||||||
|
onMounted,
|
||||||
|
onWillUnmount,
|
||||||
|
onWillUpdateProps,
|
||||||
|
onWillPatch,
|
||||||
|
onPatched,
|
||||||
|
onWillRender,
|
||||||
|
onRendered,
|
||||||
|
onWillDestroy,
|
||||||
|
onError,
|
||||||
|
} from "./component/lifecycle_hooks";
|
||||||
|
export { validate } from "./validation";
|
||||||
|
|
||||||
|
export const __info__ = {};
|
||||||
+13
-55
@@ -1,58 +1,16 @@
|
|||||||
import {
|
import { TemplateSet } from "./app/template_set";
|
||||||
config,
|
import { compile } from "./compiler";
|
||||||
createBlock,
|
|
||||||
html,
|
|
||||||
list,
|
|
||||||
mount as blockMount,
|
|
||||||
multi,
|
|
||||||
patch,
|
|
||||||
remove,
|
|
||||||
text,
|
|
||||||
toggler,
|
|
||||||
comment,
|
|
||||||
} from "./blockdom";
|
|
||||||
import { mainEventHandler } from "./component/handler";
|
|
||||||
export type { Reactive } from "./reactivity";
|
|
||||||
|
|
||||||
config.shouldNormalizeDom = false;
|
export * from "./index.runtime";
|
||||||
config.mainEventHandler = mainEventHandler;
|
|
||||||
|
|
||||||
export const blockDom = {
|
TemplateSet.prototype._compileTemplate = function _compileTemplate(
|
||||||
config,
|
name: string,
|
||||||
// bdom entry points
|
template: string | Element
|
||||||
mount: blockMount,
|
) {
|
||||||
patch,
|
return compile(template, {
|
||||||
remove,
|
name,
|
||||||
// bdom block types
|
dev: this.dev,
|
||||||
list,
|
translateFn: this.translateFn,
|
||||||
multi,
|
translatableAttributes: this.translatableAttributes,
|
||||||
text,
|
});
|
||||||
toggler,
|
|
||||||
createBlock,
|
|
||||||
html,
|
|
||||||
comment,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export { App, mount } from "./app/app";
|
|
||||||
export { xml } from "./app/template_set";
|
|
||||||
export { Component } from "./component/component";
|
|
||||||
export { useComponent, useState } from "./component/component_node";
|
|
||||||
export { status } from "./component/status";
|
|
||||||
export { reactive, markRaw, toRaw } from "./reactivity";
|
|
||||||
export { useEffect, useEnv, useExternalListener, useRef, useChildSubEnv, useSubEnv } from "./hooks";
|
|
||||||
export { EventBus, whenReady, loadFile, markup } from "./utils";
|
|
||||||
export {
|
|
||||||
onWillStart,
|
|
||||||
onMounted,
|
|
||||||
onWillUnmount,
|
|
||||||
onWillUpdateProps,
|
|
||||||
onWillPatch,
|
|
||||||
onPatched,
|
|
||||||
onWillRender,
|
|
||||||
onRendered,
|
|
||||||
onWillDestroy,
|
|
||||||
onError,
|
|
||||||
} from "./component/lifecycle_hooks";
|
|
||||||
export { validate } from "./validation";
|
|
||||||
|
|
||||||
export const __info__ = {};
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,4 @@
|
|||||||
import { onWillUnmount } from "./component/lifecycle_hooks";
|
import { onWillUnmount } from "./component/lifecycle_hooks";
|
||||||
// import { xml } from "./app/template_set";
|
|
||||||
import { BDom, text, VNode } from "./blockdom";
|
import { BDom, text, VNode } from "./blockdom";
|
||||||
import { Component } from "./component/component";
|
import { Component } from "./component/component";
|
||||||
|
|
||||||
@@ -62,6 +61,7 @@ export function portalTemplate(app: any, bdom: any, helpers: any) {
|
|||||||
return callSlot(ctx, node, key, "default", false, null);
|
return callSlot(ctx, node, key, "default", false, null);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Portal extends Component {
|
export class Portal extends Component {
|
||||||
static template = "__portal__";
|
static template = "__portal__";
|
||||||
static props = {
|
static props = {
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const jsdom = require("jsdom");
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// add global DOM stuff for compiler
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
var document = new jsdom.JSDOM("", {});
|
||||||
|
var window = document.window;
|
||||||
|
global.document = window.document;
|
||||||
|
global.window = window;
|
||||||
|
global.DOMParser = window.DOMParser;
|
||||||
|
global.Element = window.Element;
|
||||||
|
global.Node = window.Node;
|
||||||
|
// this needs to be below the jsdom stuff
|
||||||
|
const { compile } = require("../dist/compiler.js");
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// helpers
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
async function getXmlFiles(dir) {
|
||||||
|
let xmls = [];
|
||||||
|
const files = await fs.promises.readdir(dir);
|
||||||
|
const filesStats = await Promise.all(files.map((file) => fs.promises.stat(path.join(dir, file))));
|
||||||
|
for (let i in files) {
|
||||||
|
const name = path.join(dir, files[i]);
|
||||||
|
if (filesStats[i].isDirectory()) {
|
||||||
|
xmls = xmls.concat(await getXmlFiles(name));
|
||||||
|
} else {
|
||||||
|
if (name.endsWith(".xml")) {
|
||||||
|
xmls.push(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return xmls;
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeToFile(filepath, data) {
|
||||||
|
if (!fs.existsSync(path.dirname(filepath))) {
|
||||||
|
fs.mkdirSync(path.dirname(filepath), { recursive: true });
|
||||||
|
}
|
||||||
|
fs.writeFile(filepath, data, (err) => {
|
||||||
|
if (err) {
|
||||||
|
process.stdout.write(`Error while writing file ${filepath}: ${err}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// adapted from https://medium.com/@mhagemann/the-ultimate-way-to-slugify-a-url-string-in-javascript-b8e4a0d849e1
|
||||||
|
const a = "·_,:;";
|
||||||
|
const p = new RegExp(a.split("").join("|"), "g");
|
||||||
|
|
||||||
|
function slugify(str) {
|
||||||
|
return str
|
||||||
|
.replace(/\//g, "") // remove /
|
||||||
|
.replace(/\./g, "_") // Replace . with _
|
||||||
|
.replace(p, (c) => '_') // Replace special characters
|
||||||
|
.replace(/&/g, "_and_") // Replace & with ‘and’
|
||||||
|
.replace(/[^\w\-]+/g, "") // Remove all non-word characters
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// main
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function compileTemplates(files) {
|
||||||
|
process.stdout.write(`Processing ${files.length} files`);
|
||||||
|
let xmlStrings = await Promise.all(files.map((file) => fs.promises.readFile(file, "utf8")));
|
||||||
|
|
||||||
|
const templates = [];
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
const fileName = files[i];
|
||||||
|
const fileContent = xmlStrings[i];
|
||||||
|
process.stdout.write(`.`);
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(fileContent, "text/xml");
|
||||||
|
for (const template of doc.querySelectorAll("[t-name]")) {
|
||||||
|
const name = template.getAttribute("t-name");
|
||||||
|
if (template.hasAttribute("owl")) {
|
||||||
|
template.removeAttribute("owl")
|
||||||
|
}
|
||||||
|
const fnName = slugify(name);
|
||||||
|
try {
|
||||||
|
const fn = compile(template).toString().replace('anonymous', fnName);
|
||||||
|
templates.push(`owl.App.registerTemplate("${name}", ${fn});\n`);
|
||||||
|
} catch (e) {
|
||||||
|
errors.push({ name, fileName, e });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process.stdout.write(`\n`);
|
||||||
|
|
||||||
|
for (let { name, fileName, e } of errors) {
|
||||||
|
console.warn(`Error while compiling '${name}' (in file ${fileName})`);
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
console.log(`${templates.length} templates compiled`);
|
||||||
|
|
||||||
|
return templates.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
const templatesPath = process.argv[2];
|
||||||
|
if (templatesPath && templatesPath.length) {
|
||||||
|
getXmlFiles(templatesPath).then(async (files) => {
|
||||||
|
const result = await compileTemplates(files);
|
||||||
|
writeToFile("templates.js", result);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("Please provide a path");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user