mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b15327309c | |||
| 432ff444a1 | |||
| aa3c88a6c4 | |||
| 601a98e649 | |||
| 23c7d19ef0 | |||
| 59c49b5833 | |||
| 2cca0bd819 |
@@ -1,5 +1,7 @@
|
|||||||
/node_modules
|
/node_modules
|
||||||
/dist
|
/dist
|
||||||
|
# compiled devtools files
|
||||||
|
/tools/devtools/assets/*.js
|
||||||
|
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"testEnvironment": "jsdom",
|
||||||
|
"roots": [
|
||||||
|
"<rootDir>/tests"
|
||||||
|
],
|
||||||
|
"setupFiles": [
|
||||||
|
"./tests/mocks/mockEventTarget.js"
|
||||||
|
],
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.ts?$": "ts-jest"
|
||||||
|
},
|
||||||
|
"verbose": false,
|
||||||
|
"testRegex": "(/tests/.*(test|spec))\\.ts?$",
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"ts",
|
||||||
|
"tsx",
|
||||||
|
"js",
|
||||||
|
"jsx",
|
||||||
|
"json",
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
}
|
||||||
Generated
+40
@@ -1048,6 +1048,31 @@
|
|||||||
"@babel/types": "^7.3.0"
|
"@babel/types": "^7.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/chrome": {
|
||||||
|
"version": "0.0.114",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.114.tgz",
|
||||||
|
"integrity": "sha512-i7qRr74IrxHtbnrZSKUuP5Uvd5EOKwlwJq/yp7+yTPihOXnPhNQO4Z5bqb1XTnrjdbUKEJicaVVbhcgtRijmLA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/filesystem": "*",
|
||||||
|
"@types/har-format": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/filesystem": {
|
||||||
|
"version": "0.0.32",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.32.tgz",
|
||||||
|
"integrity": "sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/filewriter": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/filewriter": {
|
||||||
|
"version": "0.0.29",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.29.tgz",
|
||||||
|
"integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/fs-extra": {
|
"@types/fs-extra": {
|
||||||
"version": "8.1.2",
|
"version": "8.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.2.tgz",
|
||||||
@@ -1076,6 +1101,12 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/har-format": {
|
||||||
|
"version": "1.2.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.11.tgz",
|
||||||
|
"integrity": "sha512-T232/TneofqK30AD1LRrrf8KnjLvzrjWDp7eWST5KoiSzrBfRsLrWDPk4STQPW4NZG6v2MltnduBVmakbZOBIQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/istanbul-lib-coverage": {
|
"@types/istanbul-lib-coverage": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
|
||||||
@@ -3338,6 +3369,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jest-chrome": {
|
||||||
|
"version": "0.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-chrome/-/jest-chrome-0.8.0.tgz",
|
||||||
|
"integrity": "sha512-39RR1GT9nI4e4jsuH1vIf4l5ApxxkcstjGJr+GsOURL8f4Db0UlbRnsZaM+ZRniaGtokqklUH5VFKGZZ6YztUg==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/chrome": "^0.0.114"
|
||||||
|
}
|
||||||
|
},
|
||||||
"jest-circus": {
|
"jest-circus": {
|
||||||
"version": "27.5.1",
|
"version": "27.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz",
|
||||||
|
|||||||
+9
-28
@@ -21,15 +21,19 @@
|
|||||||
"dev:devtools-firefox": "npm run build:devtools -- --config-browser=firefox",
|
"dev:devtools-firefox": "npm run build:devtools -- --config-browser=firefox",
|
||||||
"build:devtools-chrome": "npm run dev:devtools-chrome -- --config-env=production",
|
"build:devtools-chrome": "npm run dev:devtools-chrome -- --config-env=production",
|
||||||
"build:devtools-firefox": "npm run dev:devtools-firefox -- --config-env=production",
|
"build:devtools-firefox": "npm run dev:devtools-firefox -- --config-env=production",
|
||||||
"test": "jest",
|
"test-owl": "jest",
|
||||||
"test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand --watch --testTimeout=5000000",
|
"test-owl:debug": "node --inspect-brk node_modules/.bin/jest --runInBand --watch --testTimeout=5000000",
|
||||||
"test:watch": "jest --watch",
|
"test-owl:watch": "jest --watch",
|
||||||
|
"test-devtools": "npm run build:devtools-chrome && jest --config=tools/devtools/tests/jest.config.json",
|
||||||
|
"test-devtools:debug": "npm run build:devtools-chrome && node --inspect-brk node_modules/.bin/jest --config=tools/devtools/tests/jest.config.json --runInBand --watch --testTimeout=5000000",
|
||||||
|
"test-devtools:watch": "jest --config=tools/devtools/tests/jest.config.json --watch",
|
||||||
|
"test": "npm run test-owl && npm run test-devtools",
|
||||||
"playground:serve": "python3 tools/playground_server.py || python tools/playground_server.py",
|
"playground:serve": "python3 tools/playground_server.py || python tools/playground_server.py",
|
||||||
"playground": "npm run build && npm run playground:serve",
|
"playground": "npm run build && npm run playground:serve",
|
||||||
"preplayground:watch": "npm run build",
|
"preplayground:watch": "npm run build",
|
||||||
"playground:watch": "npm-run-all --parallel playground:serve \"build:* -- --watch\"",
|
"playground:watch": "npm-run-all --parallel playground:serve \"build:* -- --watch\"",
|
||||||
"prettier": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md,tools/devtools/**/*.js} --write",
|
"prettier": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md,tools/devtools/**/*.js} --write",
|
||||||
"check-formatting": "prettier {src/*.ts,src/**/*.ts,tests/*.ts,tests/**/*.ts,doc/*.md,doc/**/*.md,tools/devtools/**/*.js} --check",
|
"check-formatting": "npm run prettier -- --check",
|
||||||
"lint": "eslint src/**/*.ts tests/**/*.ts",
|
"lint": "eslint src/**/*.ts tests/**/*.ts",
|
||||||
"release": "node tools/release.js",
|
"release": "node tools/release.js",
|
||||||
"compile_templates": "node tools/compile_xml.js"
|
"compile_templates": "node tools/compile_xml.js"
|
||||||
@@ -55,13 +59,13 @@
|
|||||||
"git-rev-sync": "^3.0.2",
|
"git-rev-sync": "^3.0.2",
|
||||||
"github-api": "^3.3.0",
|
"github-api": "^3.3.0",
|
||||||
"jest": "^27.1.0",
|
"jest": "^27.1.0",
|
||||||
|
"jest-chrome": "^0.8.0",
|
||||||
"jest-diff": "^27.3.1",
|
"jest-diff": "^27.3.1",
|
||||||
"jest-environment-jsdom": "^27.1.0",
|
"jest-environment-jsdom": "^27.1.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"prettier": "2.4.1",
|
"prettier": "2.4.1",
|
||||||
"rollup": "^2.56.3",
|
"rollup": "^2.56.3",
|
||||||
"rollup-plugin-copy": "^3.3.0",
|
"rollup-plugin-copy": "^3.3.0",
|
||||||
"rollup-plugin-delete": "^2.0.0",
|
|
||||||
"rollup-plugin-dts": "^4.2.2",
|
"rollup-plugin-dts": "^4.2.2",
|
||||||
"rollup-plugin-execute": "^1.1.1",
|
"rollup-plugin-execute": "^1.1.1",
|
||||||
"rollup-plugin-string": "^3.0.0",
|
"rollup-plugin-string": "^3.0.0",
|
||||||
@@ -71,29 +75,6 @@
|
|||||||
"ts-jest": "^27.0.5",
|
"ts-jest": "^27.0.5",
|
||||||
"typescript": "4.5.2"
|
"typescript": "4.5.2"
|
||||||
},
|
},
|
||||||
"jest": {
|
|
||||||
"testEnvironment": "jsdom",
|
|
||||||
"roots": [
|
|
||||||
"<rootDir>/src",
|
|
||||||
"<rootDir>/tests"
|
|
||||||
],
|
|
||||||
"setupFiles": [
|
|
||||||
"./tests/mocks/mockEventTarget.js"
|
|
||||||
],
|
|
||||||
"transform": {
|
|
||||||
"^.+\\.ts?$": "ts-jest"
|
|
||||||
},
|
|
||||||
"verbose": false,
|
|
||||||
"testRegex": "(/tests/.*(test|spec))\\.ts?$",
|
|
||||||
"moduleFileExtensions": [
|
|
||||||
"ts",
|
|
||||||
"tsx",
|
|
||||||
"js",
|
|
||||||
"jsx",
|
|
||||||
"json",
|
|
||||||
"node"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"prettier": {
|
"prettier": {
|
||||||
"printWidth": 100,
|
"printWidth": 100,
|
||||||
"endOfLine": "auto"
|
"endOfLine": "auto"
|
||||||
|
|||||||
@@ -36,10 +36,18 @@ export function createAttrUpdater(attr: string): Setter<HTMLElement> {
|
|||||||
|
|
||||||
export function attrsSetter(this: HTMLElement, attrs: any) {
|
export function attrsSetter(this: HTMLElement, attrs: any) {
|
||||||
if (isArray(attrs)) {
|
if (isArray(attrs)) {
|
||||||
setAttribute.call(this, attrs[0], attrs[1]);
|
if (attrs[0] === "class") {
|
||||||
|
setClass.call(this, attrs[1]);
|
||||||
|
} else {
|
||||||
|
setAttribute.call(this, attrs[0], attrs[1]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let k in attrs) {
|
for (let k in attrs) {
|
||||||
setAttribute.call(this, k, attrs[k]);
|
if (k === "class") {
|
||||||
|
setClass.call(this, attrs[k]);
|
||||||
|
} else {
|
||||||
|
setAttribute.call(this, k, attrs[k]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +60,11 @@ export function attrsUpdater(this: HTMLElement, attrs: any, oldAttrs: any) {
|
|||||||
if (val === oldAttrs[1]) {
|
if (val === oldAttrs[1]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setAttribute.call(this, name, val);
|
if (name === "class") {
|
||||||
|
updateClass.call(this, val, oldAttrs[1]);
|
||||||
|
} else {
|
||||||
|
setAttribute.call(this, name, val);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
removeAttribute.call(this, oldAttrs[0]);
|
removeAttribute.call(this, oldAttrs[0]);
|
||||||
setAttribute.call(this, name, val);
|
setAttribute.call(this, name, val);
|
||||||
@@ -60,13 +72,21 @@ export function attrsUpdater(this: HTMLElement, attrs: any, oldAttrs: any) {
|
|||||||
} else {
|
} else {
|
||||||
for (let k in oldAttrs) {
|
for (let k in oldAttrs) {
|
||||||
if (!(k in attrs)) {
|
if (!(k in attrs)) {
|
||||||
removeAttribute.call(this, k);
|
if (k === "class") {
|
||||||
|
updateClass.call(this, "", oldAttrs[k]);
|
||||||
|
} else {
|
||||||
|
removeAttribute.call(this, k);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (let k in attrs) {
|
for (let k in attrs) {
|
||||||
const val = attrs[k];
|
const val = attrs[k];
|
||||||
if (val !== oldAttrs[k]) {
|
if (val !== oldAttrs[k]) {
|
||||||
setAttribute.call(this, k, val);
|
if (k === "class") {
|
||||||
|
updateClass.call(this, val, oldAttrs[k]);
|
||||||
|
} else {
|
||||||
|
setAttribute.call(this, k, val);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,6 @@ import { OwlError } from "./error_handling";
|
|||||||
import type { ComponentNode } from "./component_node";
|
import type { ComponentNode } from "./component_node";
|
||||||
|
|
||||||
const ObjectCreate = Object.create;
|
const ObjectCreate = Object.create;
|
||||||
const ObjectGetPrototypeOf = Object.getPrototypeOf;
|
|
||||||
const ObjectGetOwnPropertyDescriptors = Object.getOwnPropertyDescriptors;
|
|
||||||
const ObjectDefineProperty = Object.defineProperty;
|
|
||||||
const ObjectEntries = Object.entries;
|
|
||||||
const hasOwnProperty = (obj: Object, prop: PropertyKey) =>
|
|
||||||
Object.prototype.hasOwnProperty.call(obj, prop);
|
|
||||||
/**
|
/**
|
||||||
* This file contains utility functions that will be injected in each template,
|
* This file contains utility functions that will be injected in each template,
|
||||||
* to perform various useful tasks in the compiled code.
|
* to perform various useful tasks in the compiled code.
|
||||||
@@ -55,14 +49,8 @@ function callSlot(
|
|||||||
|
|
||||||
function capture(ctx: any): any {
|
function capture(ctx: any): any {
|
||||||
const result = ObjectCreate(ctx);
|
const result = ObjectCreate(ctx);
|
||||||
let current = ctx;
|
for (let k in ctx) {
|
||||||
while (current && current !== Object.prototype) {
|
result[k] = ctx[k];
|
||||||
for (const [key, descriptor] of ObjectEntries(ObjectGetOwnPropertyDescriptors(current))) {
|
|
||||||
if (!hasOwnProperty(result, key) && "value" in descriptor) {
|
|
||||||
ObjectDefineProperty(result, key, descriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
current = ObjectGetPrototypeOf(current);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,3 +145,34 @@ test("class attribute (with a preexisting value", async () => {
|
|||||||
patch(tree, block([""]));
|
patch(tree, block([""]));
|
||||||
expect(fixture.innerHTML).toBe(`<div class="tomato"></div>`);
|
expect(fixture.innerHTML).toBe(`<div class="tomato"></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("block-class attributes with preexisting class attribute", async () => {
|
||||||
|
const block = createBlock('<div block-attributes="0" class="owl"></div>');
|
||||||
|
const tree = block([{ class: "eagle" }]);
|
||||||
|
|
||||||
|
mount(tree, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe(`<div class="owl eagle"></div>`);
|
||||||
|
|
||||||
|
patch(tree, block([{ class: "falcon" }]));
|
||||||
|
expect(fixture.innerHTML).toBe(`<div class="owl falcon"></div>`);
|
||||||
|
|
||||||
|
patch(tree, block([{}]));
|
||||||
|
expect(fixture.innerHTML).toBe(`<div class="owl"></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("block-class attributes (array syntax) with preexisting class attribute", async () => {
|
||||||
|
const block = createBlock('<div block-attributes="0" class="owl"></div>');
|
||||||
|
const tree = block([["class", "eagle"]]);
|
||||||
|
|
||||||
|
mount(tree, fixture);
|
||||||
|
expect(fixture.innerHTML).toBe(`<div class="owl eagle"></div>`);
|
||||||
|
|
||||||
|
patch(tree, block([["class", "falcon"]]));
|
||||||
|
expect(fixture.innerHTML).toBe(`<div class="owl falcon"></div>`);
|
||||||
|
|
||||||
|
patch(tree, block([["class", ""]]));
|
||||||
|
expect(fixture.innerHTML).toBe(`<div class="owl"></div>`);
|
||||||
|
|
||||||
|
patch(tree, block([["class", "buzzard"]]));
|
||||||
|
expect(fixture.innerHTML).toBe(`<div class="owl buzzard"></div>`);
|
||||||
|
});
|
||||||
|
|||||||
@@ -707,6 +707,123 @@ exports[`attributes updating classes (with obj notation) 1`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`attributes various combinations of class, t-att-class, and t-att 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div block-attributes=\\"0\\" class=\\"c\\">content</div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let attr1 = {class:'a'};
|
||||||
|
return block1([attr1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`attributes various combinations of class, t-att-class, and t-att 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div block-attributes=\\"0\\" block-attribute-1=\\"class\\" class=\\"c\\">content</div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let attr1 = {class:'a'};
|
||||||
|
let attr2 = {'b':true};
|
||||||
|
return block1([attr1, attr2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`attributes various combinations of class, t-att-class, and t-att 3`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div block-attributes=\\"0\\" class=\\"c\\" block-attribute-1=\\"class\\">content</div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let attr1 = {class:'a'};
|
||||||
|
let attr2 = {'b':true};
|
||||||
|
return block1([attr1, attr2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`attributes various combinations of class, t-att-class, and t-att 4`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div class=\\"c\\" block-attributes=\\"0\\" block-attribute-1=\\"class\\">content</div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let attr1 = {class:'a'};
|
||||||
|
let attr2 = {'b':true};
|
||||||
|
return block1([attr1, attr2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`attributes various combinations of class, t-att-class, and t-att 5`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div class=\\"c\\" block-attribute-0=\\"class\\" block-attributes=\\"1\\">content</div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let attr1 = {'b':true};
|
||||||
|
let attr2 = {class:'a'};
|
||||||
|
return block1([attr1, attr2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`attributes various combinations of class, t-att-class, and t-att 6`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div class=\\"c\\" block-attribute-0=\\"class\\">content</div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let attr1 = {'b':true};
|
||||||
|
return block1([attr1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`attributes various combinations of class, t-att-class, and t-att 7`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div class=\\"c\\" block-attribute-0=\\"class\\">content</div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let attr1 = ('b');
|
||||||
|
return block1([attr1]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`attributes various combinations of class, t-att-class, and t-att 8`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
let block1 = createBlock(\`<div block-attributes=\\"0\\" block-attribute-1=\\"class\\">content</div>\`);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
let attr1 = {class:'a'};
|
||||||
|
let attr2 = {'b':true};
|
||||||
|
return block1([attr1, attr2]);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`attributes various escapes 1`] = `
|
exports[`attributes various escapes 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -371,4 +371,33 @@ describe("attributes", () => {
|
|||||||
// not sure about this. maybe we want to remove the attribute?
|
// not sure about this. maybe we want to remove the attribute?
|
||||||
expect(fixture.innerHTML).toBe('<div class="hoy a b"></div>');
|
expect(fixture.innerHTML).toBe('<div class="hoy a b"></div>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("various combinations of class, t-att-class, and t-att", () => {
|
||||||
|
const template1 = `<div t-att="{ class: 'a' }" class="c">content</div>`;
|
||||||
|
expect(renderToString(template1)).toBe('<div class="c a">content</div>');
|
||||||
|
|
||||||
|
const template2 = `<div t-att="{ class: 'a' }" t-att-class="{'b': true}" class="c">content</div>`;
|
||||||
|
expect(renderToString(template2)).toBe('<div class="c a b">content</div>');
|
||||||
|
|
||||||
|
const template3 = `<div t-att="{ class: 'a' }" class="c" t-att-class="{'b': true}">content</div>`;
|
||||||
|
expect(renderToString(template3)).toBe('<div class="c a b">content</div>');
|
||||||
|
|
||||||
|
const template4 = `<div class="c" t-att="{ class: 'a' }" t-att-class="{'b': true}">content</div>`;
|
||||||
|
expect(renderToString(template4)).toBe('<div class="c a b">content</div>');
|
||||||
|
|
||||||
|
const template5 = `<div class="c" t-att-class="{'b': true}" t-att="{ class: 'a' }">content</div>`;
|
||||||
|
expect(renderToString(template5)).toBe('<div class="c b a">content</div>');
|
||||||
|
|
||||||
|
const template6 = `<div class="c" t-att-class="{'b': true}">content</div>`;
|
||||||
|
expect(renderToString(template6)).toBe('<div class="c b">content</div>');
|
||||||
|
|
||||||
|
const template7 = `<div class="c" t-attf-class="{{'b'}}">content</div>`;
|
||||||
|
expect(renderToString(template7)).toBe('<div class="c b">content</div>');
|
||||||
|
|
||||||
|
const template8 = `<div t-att="{ class: 'a' }" class="c" t-att-class="{'b': true}">content</div>`;
|
||||||
|
expect(renderToString(template8)).toBe('<div class="c a b">content</div>');
|
||||||
|
|
||||||
|
const template9 = `<div t-att="{ class: 'a' }" t-att-class="{'b': true}">content</div>`;
|
||||||
|
expect(renderToString(template9)).toBe('<div class="a b">content</div>');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,50 +1,5 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`t-call component with an enumerable getter, t-call inside slot 1`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { capture, markRaw } = helpers;
|
|
||||||
const callTemplate_1 = app.getTemplate(\`sub\`);
|
|
||||||
const comp1 = app.createComponent(\`Child\`, true, true, false, []);
|
|
||||||
|
|
||||||
function slot1(ctx, node, key = \\"\\") {
|
|
||||||
return callTemplate_1.call(this, ctx, node, key + \`__1\`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
const ctx1 = capture(ctx);
|
|
||||||
return comp1({slots: markRaw({'default': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-call component with an enumerable getter, t-call inside slot 2`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
|
|
||||||
let block1 = createBlock(\`<div><block-text-0/></div>\`);
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
let txt1 = ctx['foo'];
|
|
||||||
return block1([txt1]);
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-call component with an enumerable getter, t-call inside slot 3`] = `
|
|
||||||
"function anonymous(app, bdom, helpers
|
|
||||||
) {
|
|
||||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
|
||||||
let { callSlot } = helpers;
|
|
||||||
|
|
||||||
return function template(ctx, node, key = \\"\\") {
|
|
||||||
return callSlot(ctx, node, key, 'default', false, {});
|
|
||||||
}
|
|
||||||
}"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`t-call dynamic t-call 1`] = `
|
exports[`t-call dynamic t-call 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -425,26 +425,4 @@ describe("t-call", () => {
|
|||||||
await nextTick();
|
await nextTick();
|
||||||
expect(fixture.innerHTML).toBe("Bchild");
|
expect(fixture.innerHTML).toBe("Bchild");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("component with an enumerable getter, t-call inside slot", async () => {
|
|
||||||
class Child extends Component {
|
|
||||||
static template = xml`<t t-slot="default"/>`;
|
|
||||||
}
|
|
||||||
class Parent extends Component {
|
|
||||||
static components = { Child };
|
|
||||||
static template = xml`<Child><t t-call="sub"/></Child>`;
|
|
||||||
}
|
|
||||||
// simulate adding a getter with patch in odoo: getter will be enumarable
|
|
||||||
Object.defineProperty(Parent.prototype, "foo", {
|
|
||||||
get() {
|
|
||||||
return 1;
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
});
|
|
||||||
const app = new App(Parent);
|
|
||||||
app.addTemplate("sub", `<div t-esc="foo"/>`);
|
|
||||||
|
|
||||||
await app.mount(fixture);
|
|
||||||
expect(fixture.innerHTML).toBe("<div>1</div>");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import terser from "rollup-plugin-terser";
|
import terser from "rollup-plugin-terser";
|
||||||
import copy from "rollup-plugin-copy";
|
import copy from "rollup-plugin-copy";
|
||||||
import execute from "rollup-plugin-execute";
|
import execute from "rollup-plugin-execute";
|
||||||
import del from "rollup-plugin-delete";
|
|
||||||
import { string } from "rollup-plugin-string";
|
import { string } from "rollup-plugin-string";
|
||||||
|
|
||||||
const isWindows = process.platform === "win32";
|
const isWindows = process.platform === "win32";
|
||||||
@@ -37,12 +36,12 @@ export default ({ "config-browser": browser, "config-env": env }) => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function generateRule(input, format = "esm") {
|
function generateRule(input, plugins = []) {
|
||||||
return {
|
return {
|
||||||
input: input,
|
input,
|
||||||
output: [
|
output: [
|
||||||
{
|
{
|
||||||
format: format,
|
format: "esm",
|
||||||
file: input.replace("tools/devtools/src", "dist/devtools"),
|
file: input.replace("tools/devtools/src", "dist/devtools"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -51,6 +50,7 @@ export default ({ "config-browser": browser, "config-env": env }) => {
|
|||||||
include: "**/page_scripts/owl_devtools_global_hook.js",
|
include: "**/page_scripts/owl_devtools_global_hook.js",
|
||||||
}),
|
}),
|
||||||
isProduction && terser.terser(),
|
isProduction && terser.terser(),
|
||||||
|
...plugins,
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -58,7 +58,6 @@ export default ({ "config-browser": browser, "config-env": env }) => {
|
|||||||
commands[1] = isWindows
|
commands[1] = isWindows
|
||||||
? "npm run compile_templates -- tools\\devtools\\src && move templates.js tools\\devtools\\assets\\templates.js"
|
? "npm run compile_templates -- tools\\devtools\\src && move templates.js tools\\devtools\\assets\\templates.js"
|
||||||
: "npm run compile_templates -- tools/devtools/src && mv templates.js tools/devtools/assets/templates.js";
|
: "npm run compile_templates -- tools/devtools/src && mv templates.js tools/devtools/assets/templates.js";
|
||||||
const firstRule = generateRule("tools/devtools/src/page_scripts/owl_devtools_global_hook.js");
|
|
||||||
if (isProduction) {
|
if (isProduction) {
|
||||||
commands[0] = isWindows
|
commands[0] = isWindows
|
||||||
? "npm run build && copy dist\\owl.iife.js tools\\devtools\\assets\\owl.js && npm run build:compiler"
|
? "npm run build && copy dist\\owl.iife.js tools\\devtools\\assets\\owl.js && npm run build:compiler"
|
||||||
@@ -68,19 +67,16 @@ export default ({ "config-browser": browser, "config-env": env }) => {
|
|||||||
? "copy dist\\owl.iife.js tools\\devtools\\assets\\owl.js"
|
? "copy dist\\owl.iife.js tools\\devtools\\assets\\owl.js"
|
||||||
: "cp dist/owl.iife.js tools/devtools/assets/owl.js";
|
: "cp dist/owl.iife.js tools/devtools/assets/owl.js";
|
||||||
}
|
}
|
||||||
firstRule.plugins.push(execute(commands, true));
|
|
||||||
const secondRule = generateRule("tools/devtools/src/content.js");
|
|
||||||
secondRule.plugins.push(copy({ targets: filesToMove }));
|
|
||||||
const lastRule = generateRule("tools/devtools/src/background.js");
|
|
||||||
lastRule.plugins.push(del({ targets: "tools/devtools/assets/*.js" }));
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
firstRule,
|
generateRule("tools/devtools/src/page_scripts/owl_devtools_global_hook.js", [
|
||||||
secondRule,
|
execute(commands, true),
|
||||||
|
]),
|
||||||
|
generateRule("tools/devtools/src/content.js", [copy({ targets: filesToMove })]),
|
||||||
generateRule("tools/devtools/src/devtools_app/devtools.js"),
|
generateRule("tools/devtools/src/devtools_app/devtools.js"),
|
||||||
generateRule("tools/devtools/src/utils.js"),
|
generateRule("tools/devtools/src/utils.js"),
|
||||||
generateRule("tools/devtools/src/devtools_app/devtools_panel.js"),
|
generateRule("tools/devtools/src/devtools_app/devtools_panel.js"),
|
||||||
generateRule("tools/devtools/src/popup_app/popup.js"),
|
generateRule("tools/devtools/src/popup_app/popup.js"),
|
||||||
lastRule,
|
generateRule("tools/devtools/src/background.js"),
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
import { DevtoolsWindow } from "./devtools_window/devtools_window";
|
import { DevtoolsWindow } from "./devtools_window/devtools_window";
|
||||||
const { mount } = owl;
|
const { mount } = owl;
|
||||||
import { templates } from "../../assets/templates.js";
|
import { templates } from "../../assets/templates.js";
|
||||||
|
import { createStore } from "./store/store";
|
||||||
|
|
||||||
for (const template in templates) {
|
for (const template in templates) {
|
||||||
owl.App.registerTemplate(template, templates[template]);
|
owl.App.registerTemplate(template, templates[template]);
|
||||||
}
|
}
|
||||||
|
createStore();
|
||||||
mount(DevtoolsWindow, document.body, { dev: true });
|
mount(DevtoolsWindow, document.body, { dev: true });
|
||||||
|
|||||||
+1
-2
@@ -1,11 +1,10 @@
|
|||||||
const { Component, useRef, useEffect } = owl;
|
const { Component, useRef, useEffect } = owl;
|
||||||
import { useStore } from "../../../store/store";
|
import { useStore } from "../../../store/store";
|
||||||
import { ObjectTreeElement } from "./object_tree_element/object_tree_element";
|
import { ObjectTreeElement } from "./object_tree_element/object_tree_element";
|
||||||
import { Subscriptions } from "./subscriptions/subscriptions";
|
|
||||||
|
|
||||||
export class DetailsWindow extends Component {
|
export class DetailsWindow extends Component {
|
||||||
static template = "devtools.DetailsWindow";
|
static template = "devtools.DetailsWindow";
|
||||||
static components = { ObjectTreeElement, Subscriptions };
|
static components = { ObjectTreeElement };
|
||||||
setup() {
|
setup() {
|
||||||
this.store = useStore();
|
this.store = useStore();
|
||||||
this.contextMenu = useRef("contextmenu");
|
this.contextMenu = useRef("contextmenu");
|
||||||
|
|||||||
+5
-1
@@ -53,7 +53,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<i title="Store observed states as global variable in the console" class="fa fa-bug utility-icon p-1" t-on-click.stop="() => this.store.logObjectInConsole([...this.store.activeComponent.path, {type: 'item', value: 'subscriptions'}])"></i>
|
<i title="Store observed states as global variable in the console" class="fa fa-bug utility-icon p-1" t-on-click.stop="() => this.store.logObjectInConsole([...this.store.activeComponent.path, {type: 'item', value: 'subscriptions'}])"></i>
|
||||||
</div>
|
</div>
|
||||||
<Subscriptions t-if="store.activeComponent.subscriptions.toggled"/>
|
<div t-if="store.activeComponent.subscriptions.toggled" id="subscriptionsPanel">
|
||||||
|
<t t-foreach="store.activeComponent.subscriptions.children" t-as="subscription" t-key="subscription_index">
|
||||||
|
<ObjectTreeElement object="subscription.target"/>
|
||||||
|
</t>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div t-if="store.activeComponent.instance.children.length > 0" id="instance" class="details-panel ps-2 py-1">
|
<div t-if="store.activeComponent.instance.children.length > 0" id="instance" class="details-panel ps-2 py-1">
|
||||||
<div class="d-flex mb-2">
|
<div class="d-flex mb-2">
|
||||||
|
|||||||
+9
-10
@@ -41,23 +41,22 @@ export class ObjectTreeElement extends Component {
|
|||||||
return JSON.stringify(this.props.object.path);
|
return JSON.stringify(this.props.object.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
get objectName() {
|
get keyChanges() {
|
||||||
return this.props.object.name;
|
return this.props.object.keys?.includes("Symbol(Key changes)");
|
||||||
}
|
}
|
||||||
|
|
||||||
get objectLineClass() {
|
classFor(object) {
|
||||||
// Prototype items will be dyed down to appear less important
|
// Prototype items will be dyed down to appear less important
|
||||||
if (this.pathAsString.includes('{"type":"prototype",')) {
|
if (object.path.some((item) => item?.type === "prototype")) {
|
||||||
return { attenuate: true };
|
return "attenuate";
|
||||||
}
|
}
|
||||||
// Same for subscription items which are not present in the keys while the keys will be bold
|
// Same for subscription items which are not present in the keys while the keys will be bold
|
||||||
if (this.props.object.objectType === "subscription" && this.props.object.depth > 0) {
|
if (object.objectType === "subscription" && object.depth > 0) {
|
||||||
if (this.props.keys.includes(this.props.object.name.toString())) {
|
if (this.props.object.keys?.includes(object.name.toString())) {
|
||||||
return { "fw-bolder": true };
|
return "fw-bolder";
|
||||||
}
|
}
|
||||||
return { attenuate: true };
|
return "attenuate";
|
||||||
}
|
}
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get objectPadding() {
|
get objectPadding() {
|
||||||
|
|||||||
+4
-4
@@ -2,7 +2,7 @@
|
|||||||
<templates xml:space="preserve">
|
<templates xml:space="preserve">
|
||||||
<t t-name="devtools.ObjectTreeElement" owl="1">
|
<t t-name="devtools.ObjectTreeElement" owl="1">
|
||||||
<div class="m-0 p-0 text-nowrap w-100 object-line"
|
<div class="m-0 p-0 text-nowrap w-100 object-line"
|
||||||
t-att-class="objectLineClass"
|
t-att-class="props.class"
|
||||||
t-on-click.stop="() => this.store.toggleObjectTreeElementsDisplay(this.props.object)"
|
t-on-click.stop="() => this.store.toggleObjectTreeElementsDisplay(this.props.object)"
|
||||||
t-on-contextmenu.prevent="openMenu"
|
t-on-contextmenu.prevent="openMenu"
|
||||||
>
|
>
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
t-att-class="{'fa-caret-right': !props.object.toggled, 'fa-caret-down': props.object.toggled}"
|
t-att-class="{'fa-caret-right': !props.object.toggled, 'fa-caret-down': props.object.toggled}"
|
||||||
t-attf-style="visibility: {{props.object.hasChildren ? '' : 'hidden'}};"
|
t-attf-style="visibility: {{props.object.hasChildren ? '' : 'hidden'}};"
|
||||||
/>
|
/>
|
||||||
<t t-esc="objectName"/>
|
<t t-esc="props.object.name"/>
|
||||||
<t t-if="props.object.content.length > 0">: </t>
|
<t t-if="props.object.content.length > 0">: </t>
|
||||||
<t t-if="props.object.contentType == 'getter'">
|
<t t-if="props.object.contentType == 'getter'">
|
||||||
<span class="getter-content object-content" t-att-class="objectLineClass" t-on-click.stop="() => this.store.loadGetterContent(this.props.object)">
|
<span class="getter-content object-content" t-att-class="objectLineClass" t-on-click.stop="() => this.store.loadGetterContent(this.props.object)">
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
</t>
|
</t>
|
||||||
</span>
|
</span>
|
||||||
</t>
|
</t>
|
||||||
|
<span t-if="keyChanges" class="key-changes ms-1 badge p-1" title="Key additions/deletions are observed">+/-</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div t-if="store.contextMenu.activeMenu === contextMenuId" class="custom-menu" t-ref="contextmenu">
|
<div t-if="store.contextMenu.activeMenu === contextMenuId" class="custom-menu" t-ref="contextmenu">
|
||||||
@@ -40,8 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<t t-if="props.object.toggled" t-key="contextMenuId">
|
<t t-if="props.object.toggled" t-key="contextMenuId">
|
||||||
<t t-foreach="props.object.children" t-as="child" t-key="child_index">
|
<t t-foreach="props.object.children" t-as="child" t-key="child_index">
|
||||||
<ObjectTreeElement t-if="props.object.objectType === 'subscription'" object="child" keys="props.keys"/>
|
<ObjectTreeElement object="child" class="this.classFor(child)"/>
|
||||||
<ObjectTreeElement t-else="" object="child"/>
|
|
||||||
</t>
|
</t>
|
||||||
</t>
|
</t>
|
||||||
</t>
|
</t>
|
||||||
|
|||||||
-30
@@ -1,30 +0,0 @@
|
|||||||
const { Component } = owl;
|
|
||||||
import { useStore } from "../../../../store/store";
|
|
||||||
import { ObjectTreeElement } from "../object_tree_element/object_tree_element";
|
|
||||||
|
|
||||||
export class Subscriptions extends Component {
|
|
||||||
static template = "devtools.Subscriptions";
|
|
||||||
|
|
||||||
static components = { ObjectTreeElement };
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
this.store = useStore();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used to display the keys in a compact way
|
|
||||||
keysContent(index) {
|
|
||||||
const keys = this.store.activeComponent.subscriptions.children[index].keys;
|
|
||||||
let content = JSON.stringify(keys);
|
|
||||||
const maxLength = 50;
|
|
||||||
content = content.replace(/,/g, ", ");
|
|
||||||
if (content.length > maxLength) {
|
|
||||||
content = content.slice(0, content.lastIndexOf(",", maxLength - 5)) + ", ...]";
|
|
||||||
}
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
expandKeys(event, index) {
|
|
||||||
this.store.activeComponent.subscriptions.children[index].keysExpanded =
|
|
||||||
!this.store.activeComponent.subscriptions.children[index].keysExpanded;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-24
@@ -1,24 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<templates xml:space="preserve">
|
|
||||||
<t t-name="devtools.Subscriptions" owl="1">
|
|
||||||
<div id="subscriptionsPanel">
|
|
||||||
<t t-foreach="store.activeComponent.subscriptions.children" t-as="subscription" t-key="subscription_index">
|
|
||||||
<div class="my-2">
|
|
||||||
<div class="my-0 p-0 object-line" t-on-click.stop="(ev) => this.expandKeys(ev, subscription_index)">
|
|
||||||
<span class="ps-1 text-nowrap">
|
|
||||||
<i class="fa fa-caret-right ms-1" t-attf-style="cursor: pointer;{{subscription.keysExpanded ? 'transform: rotate(90deg);' : ''}}"></i>
|
|
||||||
keys: <span class="key-name"><t t-esc="this.keysContent(subscription_index)"/></span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div t-foreach="subscription.keys" t-as="key" t-key="key_index" class="my-0 p-0 object-line" t-attf-style="display: {{subscription.keysExpanded ? 'flex' : 'none'}}">
|
|
||||||
<div style="transform: translateX(calc(1.1rem))" class="key-content">
|
|
||||||
<i class="fa fa-caret-right mx-1" t-attf-style="cursor: pointer; visibility: hidden;"></i>
|
|
||||||
<t t-esc="key"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ObjectTreeElement object="subscription.target" keys="subscription.keys"/>
|
|
||||||
</div>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</t>
|
|
||||||
</templates>
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<templates xml:space="preserve">
|
<templates xml:space="preserve">
|
||||||
<t t-name="devtools.Event" owl="1">
|
<t t-name="devtools.Event" owl="1">
|
||||||
<div class="event-container">
|
<div class="event-container" t-att-class="{ 'event-last': props.event.isLast }">
|
||||||
<div class="my-0 p-0 object-line" t-on-click.stop="toggleDisplay">
|
<div class="my-0 p-0 object-line" t-on-click.stop="toggleDisplay">
|
||||||
<div class="ps-2 text-nowrap">
|
<div class="ps-2 text-nowrap">
|
||||||
<i class="fa px-1 pointer-icon caret"
|
<i class="fa px-1 pointer-icon caret"
|
||||||
|
|||||||
@@ -102,20 +102,17 @@ export const store = reactive({
|
|||||||
if (IS_FIREFOX) {
|
if (IS_FIREFOX) {
|
||||||
await evalInWindow("window.$0 = $0;", this.activeFrame);
|
await evalInWindow("window.$0 = $0;", this.activeFrame);
|
||||||
}
|
}
|
||||||
const apps = await evalFunctionInWindow(
|
const [apps, component] = await evalFunctionInWindow(
|
||||||
"getComponentsTree",
|
"getComponentsTree",
|
||||||
fromOld && this.activeComponent ? [this.activeComponent.path, this.apps] : [],
|
fromOld && this.activeComponent
|
||||||
|
? [this.activeComponent.path, this.apps, this.activeComponent]
|
||||||
|
: [],
|
||||||
this.activeFrame
|
this.activeFrame
|
||||||
);
|
);
|
||||||
this.apps = apps ? apps : [];
|
this.apps = apps ? apps : [];
|
||||||
if (!fromOld && this.settings.expandByDefault) {
|
if (!fromOld && this.settings.expandByDefault) {
|
||||||
this.apps.forEach((tree) => expandNodes(tree, true));
|
this.apps.forEach((tree) => expandNodes(tree, true));
|
||||||
}
|
}
|
||||||
const component = await evalFunctionInWindow(
|
|
||||||
"getComponentDetails",
|
|
||||||
fromOld && this.activeComponent ? [this.activeComponent.path, this.activeComponent] : [],
|
|
||||||
this.activeFrame
|
|
||||||
);
|
|
||||||
this.activeComponent = component;
|
this.activeComponent = component;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -413,12 +410,7 @@ export const store = reactive({
|
|||||||
if (!scriptsLoaded) {
|
if (!scriptsLoaded) {
|
||||||
await loadScripts(frame);
|
await loadScripts(frame);
|
||||||
}
|
}
|
||||||
evalInWindow(
|
evalFunctionInWindow("initDevtools", [frame], frame);
|
||||||
`__OWL__DEVTOOLS_GLOBAL_HOOK__.devtoolsId = ${
|
|
||||||
store.devtoolsId
|
|
||||||
}; __OWL__DEVTOOLS_GLOBAL_HOOK__.frame = ${JSON.stringify(frame)};`,
|
|
||||||
frame
|
|
||||||
);
|
|
||||||
if (!this.frameUrls.includes(frame)) {
|
if (!this.frameUrls.includes(frame)) {
|
||||||
this.frameUrls = [...this.frameUrls, frame];
|
this.frameUrls = [...this.frameUrls, frame];
|
||||||
}
|
}
|
||||||
@@ -633,12 +625,10 @@ export function useStore() {
|
|||||||
return useState(store);
|
return useState(store);
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
export async function createStore() {
|
||||||
|
|
||||||
async function init() {
|
|
||||||
store.devtoolsId = await getTabURL();
|
store.devtoolsId = await getTabURL();
|
||||||
|
|
||||||
evalInWindow("__OWL__DEVTOOLS_GLOBAL_HOOK__.devtoolsId = " + store.devtoolsId + ";");
|
evalFunctionInWindow("initDevtools", []);
|
||||||
|
|
||||||
await loadSettings();
|
await loadSettings();
|
||||||
|
|
||||||
@@ -671,7 +661,7 @@ async function init() {
|
|||||||
}, 500);
|
}, 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
let flushRendersTimeout = false;
|
let rootRendersTimeout = false;
|
||||||
// Connect to the port to communicate to the background script
|
// Connect to the port to communicate to the background script
|
||||||
browserInstance.runtime.onConnect.addListener((port) => {
|
browserInstance.runtime.onConnect.addListener((port) => {
|
||||||
if (port.name === "OwlDevtoolsPort_" + store.devtoolsId) {
|
if (port.name === "OwlDevtoolsPort_" + store.devtoolsId) {
|
||||||
@@ -680,7 +670,7 @@ browserInstance.runtime.onConnect.addListener((port) => {
|
|||||||
if (msg.type === "Reload") {
|
if (msg.type === "Reload") {
|
||||||
store.owlStatus = await evalInWindow("window.__OWL__DEVTOOLS_GLOBAL_HOOK__ !== undefined;");
|
store.owlStatus = await evalInWindow("window.__OWL__DEVTOOLS_GLOBAL_HOOK__ !== undefined;");
|
||||||
if (store.owlStatus) {
|
if (store.owlStatus) {
|
||||||
evalInWindow("__OWL__DEVTOOLS_GLOBAL_HOOK__.devtoolsId = " + store.devtoolsId + ";");
|
evalFunctionInWindow("initDevtools", []);
|
||||||
await store.resetData();
|
await store.resetData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -694,9 +684,9 @@ browserInstance.runtime.onConnect.addListener((port) => {
|
|||||||
if (msg.type === "RefreshApps") {
|
if (msg.type === "RefreshApps") {
|
||||||
store.loadComponentsTree(true);
|
store.loadComponentsTree(true);
|
||||||
}
|
}
|
||||||
// When message of type Flush is received, overwrite the component tree with the new one from page
|
// When message of type Complete is received, overwrite the component tree with the new one from page
|
||||||
// A flush message is sent everytime a component is rendered on the page
|
// A Complete message is sent everytime a root render is triggered on the page
|
||||||
if (msg.type === "Flush") {
|
if (msg.type === "Complete") {
|
||||||
if (msg.origin.frame !== store.activeFrame) {
|
if (msg.origin.frame !== store.activeFrame) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -705,8 +695,8 @@ browserInstance.runtime.onConnect.addListener((port) => {
|
|||||||
}
|
}
|
||||||
// This determines which components will have a short highlight effect in the tree to indicate they have been rendered
|
// This determines which components will have a short highlight effect in the tree to indicate they have been rendered
|
||||||
store.renderPaths.add(JSON.stringify(msg.data));
|
store.renderPaths.add(JSON.stringify(msg.data));
|
||||||
clearTimeout(flushRendersTimeout);
|
clearTimeout(rootRendersTimeout);
|
||||||
flushRendersTimeout = setTimeout(() => {
|
rootRendersTimeout = setTimeout(() => {
|
||||||
store.renderPaths.clear();
|
store.renderPaths.clear();
|
||||||
}, 100);
|
}, 100);
|
||||||
store.loadComponentsTree(true);
|
store.loadComponentsTree(true);
|
||||||
@@ -787,6 +777,7 @@ function loadEvents(events) {
|
|||||||
}
|
}
|
||||||
event.origin = null;
|
event.origin = null;
|
||||||
event.toggled = false;
|
event.toggled = false;
|
||||||
|
event.isLast = false;
|
||||||
// Logic to retrace the origin of the event if it is not a root render event
|
// Logic to retrace the origin of the event if it is not a root render event
|
||||||
if (!event.type.includes("render")) {
|
if (!event.type.includes("render")) {
|
||||||
for (let i = store.events.length - 1; i >= 0; i--) {
|
for (let i = store.events.length - 1; i >= 0; i--) {
|
||||||
@@ -836,6 +827,7 @@ function loadEvents(events) {
|
|||||||
// Make sure we add the event while keeping the whole list ordered by id
|
// Make sure we add the event while keeping the whole list ordered by id
|
||||||
addEventSorted(event);
|
addEventSorted(event);
|
||||||
}
|
}
|
||||||
|
store.events[store.events.length - 1].isLast = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deselect component and remove highlight on all children
|
// Deselect component and remove highlight on all children
|
||||||
|
|||||||
@@ -126,6 +126,10 @@
|
|||||||
color: var(--prototype-color);
|
color: var(--prototype-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.key-changes {
|
||||||
|
background-color: var(--version-bg);
|
||||||
|
}
|
||||||
|
|
||||||
.event-container {
|
.event-container {
|
||||||
border-bottom: 1px solid rgb(240, 238, 238);
|
border-bottom: 1px solid rgb(240, 238, 238);
|
||||||
padding-top: 2px!important;
|
padding-top: 2px!important;
|
||||||
@@ -133,6 +137,10 @@
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.event-last {
|
||||||
|
border-bottom: 3px solid rgb(225, 154, 0);
|
||||||
|
}
|
||||||
|
|
||||||
.getter-content:hover {
|
.getter-content:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,6 @@
|
|||||||
// in __OWL_DEVTOOLS__
|
// in __OWL_DEVTOOLS__
|
||||||
this.toRaw = window.__OWL_DEVTOOLS__.toRaw ?? window.owl?.toRaw;
|
this.toRaw = window.__OWL_DEVTOOLS__.toRaw ?? window.owl?.toRaw;
|
||||||
this.reactive = window.__OWL_DEVTOOLS__.reactive ?? window.owl?.reactive;
|
this.reactive = window.__OWL_DEVTOOLS__.reactive ?? window.owl?.reactive;
|
||||||
// Set to keep track of the fibers that are in the flush queue
|
|
||||||
this.queuedFibers = new WeakSet();
|
|
||||||
// Set to keep track of the HTML elements we added to the page
|
// Set to keep track of the HTML elements we added to the page
|
||||||
this.addedElements = [];
|
this.addedElements = [];
|
||||||
// To keep track of the succession order of the render events
|
// To keep track of the succession order of the render events
|
||||||
@@ -24,7 +22,6 @@
|
|||||||
// Set to keep track of the frame on which this script is loaded
|
// Set to keep track of the frame on which this script is loaded
|
||||||
this.frame = "top";
|
this.frame = "top";
|
||||||
// Allows to launch a message each time an iframe html element is added to the page
|
// Allows to launch a message each time an iframe html element is added to the page
|
||||||
const self = this;
|
|
||||||
const iFrameObserver = new MutationObserver(function (mutationsList) {
|
const iFrameObserver = new MutationObserver(function (mutationsList) {
|
||||||
mutationsList.forEach(function (mutation) {
|
mutationsList.forEach(function (mutation) {
|
||||||
mutation.addedNodes.forEach(function (addedNode) {
|
mutation.addedNodes.forEach(function (addedNode) {
|
||||||
@@ -36,23 +33,21 @@
|
|||||||
* This process may seem long and indirect but is necessary. This applies to all window.top.postMessage methods in this file.
|
* This process may seem long and indirect but is necessary. This applies to all window.top.postMessage methods in this file.
|
||||||
* More information in the docs: https://developer.chrome.com/docs/extensions/mv3/devtools/#evaluated-scripts-to-devtools
|
* More information in the docs: https://developer.chrome.com/docs/extensions/mv3/devtools/#evaluated-scripts-to-devtools
|
||||||
*/
|
*/
|
||||||
window.top.postMessage({
|
window.top.postMessage(
|
||||||
source: "owl-devtools",
|
{
|
||||||
type: "NewIFrame",
|
source: "owl-devtools",
|
||||||
data: addedNode.contentDocument.location.href,
|
type: "NewIFrame",
|
||||||
});
|
data: addedNode.contentDocument.location.href,
|
||||||
|
},
|
||||||
|
"/"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
iFrameObserver.observe(document.body, { subtree: true, childList: true });
|
iFrameObserver.observe(document.body, { subtree: true, childList: true });
|
||||||
this.appsPatched = false;
|
|
||||||
this.destroyPatched = false;
|
|
||||||
this.patchAppsSetMethods();
|
this.patchAppsSetMethods();
|
||||||
if (this.apps.size > 0) {
|
|
||||||
this.patchAppMethods();
|
|
||||||
}
|
|
||||||
this.recordEvents = false;
|
this.recordEvents = false;
|
||||||
this.traceRenderings = false;
|
this.traceRenderings = false;
|
||||||
this.traceSubscriptions = false;
|
this.traceSubscriptions = false;
|
||||||
@@ -172,114 +167,84 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initDevtools(frame = "top") {
|
||||||
|
if (!this.devtoolsInit) {
|
||||||
|
this.frame = frame;
|
||||||
|
const self = this;
|
||||||
|
// Flush the events batcher when a root render is completed
|
||||||
|
const original_Complete = self.RootFiber.prototype.complete;
|
||||||
|
self.RootFiber.prototype.complete = function () {
|
||||||
|
original_Complete.call(this, ...arguments);
|
||||||
|
const path = self.getComponentPath(this.node);
|
||||||
|
//Add a functionnality to the complete function which sends a message to the window every time it is triggered.
|
||||||
|
window.top.postMessage(
|
||||||
|
{
|
||||||
|
source: "owl-devtools",
|
||||||
|
type: "Complete",
|
||||||
|
data: path,
|
||||||
|
origin: { frame: self.frame },
|
||||||
|
},
|
||||||
|
"/"
|
||||||
|
);
|
||||||
|
if (self.recordEvents) {
|
||||||
|
window.top.postMessage(
|
||||||
|
{
|
||||||
|
source: "owl-devtools",
|
||||||
|
type: "Event",
|
||||||
|
data: self.eventsBatch,
|
||||||
|
},
|
||||||
|
"/"
|
||||||
|
);
|
||||||
|
self.eventsBatch = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.devtoolsInit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Modify the methods of the apps set in order to send a message each time it is modified.
|
// Modify the methods of the apps set in order to send a message each time it is modified.
|
||||||
patchAppsSetMethods() {
|
patchAppsSetMethods() {
|
||||||
const originalAdd = this.apps.add;
|
const originalAdd = this.apps.add;
|
||||||
const originalDelete = this.apps.delete;
|
const originalDelete = this.apps.delete;
|
||||||
const self = this;
|
|
||||||
this.apps.add = function () {
|
this.apps.add = function () {
|
||||||
originalAdd.call(this, ...arguments);
|
originalAdd.call(this, ...arguments);
|
||||||
if (!self.destroyPatched) {
|
window.top.postMessage(
|
||||||
const newApp = arguments[0];
|
{
|
||||||
// It is not a given that apps have a root node at creation so we need to wait
|
source: "owl-devtools",
|
||||||
if (newApp.root) {
|
type: "RefreshApps",
|
||||||
self.patchDestroyMethod(newApp.root);
|
},
|
||||||
} else {
|
"/"
|
||||||
let root = null;
|
);
|
||||||
Object.defineProperty(newApp, "root", {
|
|
||||||
get() {
|
|
||||||
return root;
|
|
||||||
},
|
|
||||||
set(value) {
|
|
||||||
root = value;
|
|
||||||
if (!self.destroyPatched) {
|
|
||||||
self.patchDestroyMethod(root);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.patchAppMethods();
|
|
||||||
window.top.postMessage({
|
|
||||||
source: "owl-devtools",
|
|
||||||
type: "RefreshApps",
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
this.apps.delete = function () {
|
this.apps.delete = function () {
|
||||||
originalDelete.call(this, ...arguments);
|
originalDelete.call(this, ...arguments);
|
||||||
window.top.postMessage({
|
window.top.postMessage(
|
||||||
source: "owl-devtools",
|
{
|
||||||
type: "RefreshApps",
|
source: "owl-devtools",
|
||||||
});
|
type: "RefreshApps",
|
||||||
|
},
|
||||||
|
"/"
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
patchDestroyMethod(root) {
|
|
||||||
if (!this.destroyPatched) {
|
|
||||||
// Signals when a component is destroyed
|
|
||||||
const originalDestroy = root.constructor.prototype._destroy;
|
|
||||||
const self = this;
|
|
||||||
root.constructor.prototype._destroy = function () {
|
|
||||||
if (self.recordEvents) {
|
|
||||||
const path = self.getComponentPath(this);
|
|
||||||
const event = {
|
|
||||||
type: "destroy",
|
|
||||||
component: this.name,
|
|
||||||
key: this.parentKey,
|
|
||||||
path: path,
|
|
||||||
time: 0,
|
|
||||||
id: self.eventId++,
|
|
||||||
};
|
|
||||||
self.eventsBatch.push(event);
|
|
||||||
const before = performance.now();
|
|
||||||
originalDestroy.call(this, ...arguments);
|
|
||||||
event.time = performance.now() - before;
|
|
||||||
} else {
|
|
||||||
originalDestroy.call(this, ...arguments);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.destroyPatched = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modify methods of each app so that it triggers messages on each flush and component render
|
// Modify methods of each app so that it triggers messages on each flush and component render
|
||||||
patchAppMethods() {
|
patchAppMethods() {
|
||||||
if (this.appsPatched) {
|
let app;
|
||||||
|
for (const appItem of this.apps) {
|
||||||
|
if (appItem.root) {
|
||||||
|
app = appItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!app.root) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let app = this.apps.values().next().value;
|
|
||||||
const self = this;
|
const self = this;
|
||||||
if (app.root) {
|
|
||||||
this.patchDestroyMethod(app.root);
|
|
||||||
} else {
|
|
||||||
const originalMount = app.constructor.prototype.mount;
|
|
||||||
app.constructor.prototype.mount = async function (...args) {
|
|
||||||
const result = await originalMount.call(this, ...args);
|
|
||||||
const root = this.root;
|
|
||||||
self.patchDestroyMethod(root);
|
|
||||||
app.constructor.prototype.mount = originalMount;
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const originalFlush = app.scheduler.constructor.prototype.flush;
|
const originalFlush = app.scheduler.constructor.prototype.flush;
|
||||||
let inFlush = false;
|
let inFlush = false;
|
||||||
let _render = false;
|
let _render = false;
|
||||||
app.scheduler.constructor.prototype.flush = function () {
|
app.scheduler.constructor.prototype.flush = function () {
|
||||||
// Used to know when a render is triggered inside the flush method or not
|
// Used to know when a render is triggered inside the flush method or not
|
||||||
inFlush = true;
|
inFlush = true;
|
||||||
[...this.tasks].map((fiber) => {
|
|
||||||
if (fiber.counter === 0 && !self.queuedFibers.has(fiber)) {
|
|
||||||
self.queuedFibers.add(fiber);
|
|
||||||
const path = self.getComponentPath(fiber.node);
|
|
||||||
//Add a functionnality to the flush function which sends a message to the window every time it is triggered.
|
|
||||||
window.top.postMessage({
|
|
||||||
source: "owl-devtools",
|
|
||||||
type: "Flush",
|
|
||||||
data: path,
|
|
||||||
origin: { frame: self.frame },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
originalFlush.call(this, ...arguments);
|
originalFlush.call(this, ...arguments);
|
||||||
inFlush = false;
|
inFlush = false;
|
||||||
};
|
};
|
||||||
@@ -376,20 +341,27 @@
|
|||||||
_render = true;
|
_render = true;
|
||||||
original_Render.call(this, ...arguments);
|
original_Render.call(this, ...arguments);
|
||||||
};
|
};
|
||||||
// Flush the events batcher when a root render is completed
|
// Signals when a component is destroyed
|
||||||
const original_Complete = self.RootFiber.prototype.complete;
|
const originalDestroy = app.root.constructor.prototype._destroy;
|
||||||
self.RootFiber.prototype.complete = function () {
|
app.root.constructor.prototype._destroy = function () {
|
||||||
original_Complete.call(this, ...arguments);
|
|
||||||
if (self.recordEvents) {
|
if (self.recordEvents) {
|
||||||
window.top.postMessage({
|
const path = self.getComponentPath(this);
|
||||||
source: "owl-devtools",
|
const event = {
|
||||||
type: "Event",
|
type: "destroy",
|
||||||
data: self.eventsBatch,
|
component: this.name,
|
||||||
});
|
key: this.parentKey,
|
||||||
self.eventsBatch = [];
|
path: path,
|
||||||
|
time: 0,
|
||||||
|
id: self.eventId++,
|
||||||
|
};
|
||||||
|
self.eventsBatch.push(event);
|
||||||
|
const before = performance.now();
|
||||||
|
originalDestroy.call(this, ...arguments);
|
||||||
|
event.time = performance.now() - before;
|
||||||
|
} else {
|
||||||
|
originalDestroy.call(this, ...arguments);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.appsPatched = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// patch reactivity system to activate subscription tracing
|
// patch reactivity system to activate subscription tracing
|
||||||
@@ -453,9 +425,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
toggleTracing(value) {
|
toggleTracing(value) {
|
||||||
|
if (value) {
|
||||||
|
this.patchAppMethods();
|
||||||
|
this.patchAppMethods = () => {}; // to only patch once
|
||||||
|
}
|
||||||
this.traceRenderings = value;
|
this.traceRenderings = value;
|
||||||
return this.traceRenderings;
|
return this.traceRenderings;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleSubscriptionTracing(value) {
|
toggleSubscriptionTracing(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
this.patchReactivity();
|
this.patchReactivity();
|
||||||
@@ -466,6 +443,10 @@
|
|||||||
}
|
}
|
||||||
// Enables/disables the recording of the render/destroy events based on value
|
// Enables/disables the recording of the render/destroy events based on value
|
||||||
toggleEventsRecording(value, index) {
|
toggleEventsRecording(value, index) {
|
||||||
|
if (value) {
|
||||||
|
this.patchAppMethods();
|
||||||
|
this.patchAppMethods = () => {}; // to only patch once
|
||||||
|
}
|
||||||
this.recordEvents = value;
|
this.recordEvents = value;
|
||||||
this.eventId = index;
|
this.eventId = index;
|
||||||
return this.recordEvents;
|
return this.recordEvents;
|
||||||
@@ -636,11 +617,14 @@
|
|||||||
const path = this.getElementPath(target);
|
const path = this.getElementPath(target);
|
||||||
this.highlightComponent(path);
|
this.highlightComponent(path);
|
||||||
this.currentSelectedElement = target;
|
this.currentSelectedElement = target;
|
||||||
window.top.postMessage({
|
window.top.postMessage(
|
||||||
source: "owl-devtools",
|
{
|
||||||
type: "SelectElement",
|
source: "owl-devtools",
|
||||||
data: path,
|
type: "SelectElement",
|
||||||
});
|
data: path,
|
||||||
|
},
|
||||||
|
"/"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -664,10 +648,13 @@
|
|||||||
document.removeEventListener("mouseover", this.HTMLSelector, { capture: true });
|
document.removeEventListener("mouseover", this.HTMLSelector, { capture: true });
|
||||||
document.removeEventListener("click", this.disableHTMLSelector, { capture: true });
|
document.removeEventListener("click", this.disableHTMLSelector, { capture: true });
|
||||||
document.removeEventListener("mouseout", this.removeHighlights, { capture: true });
|
document.removeEventListener("mouseout", this.removeHighlights, { capture: true });
|
||||||
window.top.postMessage({
|
window.top.postMessage(
|
||||||
source: "owl-devtools",
|
{
|
||||||
type: "StopSelector",
|
source: "owl-devtools",
|
||||||
});
|
type: "StopSelector",
|
||||||
|
},
|
||||||
|
"/"
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns the object specified by the path starting from the topParent object
|
// Returns the object specified by the path starting from the topParent object
|
||||||
@@ -819,57 +806,47 @@
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (child.contentType) {
|
if (!child.contentType) {
|
||||||
if (child.toggled) {
|
if (obj === null) {
|
||||||
child.children = this.loadObjectChildren(
|
child.content = "null";
|
||||||
child.path,
|
child.contentType = "object";
|
||||||
child.depth,
|
child.hasChildren = false;
|
||||||
child.contentType,
|
} else if (obj === undefined) {
|
||||||
child.objectType,
|
child.content = "undefined";
|
||||||
oldTree
|
child.contentType = "undefined";
|
||||||
);
|
child.hasChildren = false;
|
||||||
}
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
if (obj === null) {
|
|
||||||
child.content = "null";
|
|
||||||
child.contentType = "object";
|
|
||||||
child.hasChildren = false;
|
|
||||||
} else if (obj === undefined) {
|
|
||||||
child.content = "undefined";
|
|
||||||
child.contentType = "undefined";
|
|
||||||
child.hasChildren = false;
|
|
||||||
} else {
|
|
||||||
obj = this.toRaw(obj);
|
|
||||||
switch (true) {
|
|
||||||
case obj instanceof Map:
|
|
||||||
child.contentType = "map";
|
|
||||||
child.hasChildren = true;
|
|
||||||
break;
|
|
||||||
case obj instanceof Set:
|
|
||||||
child.contentType = "set";
|
|
||||||
child.hasChildren = true;
|
|
||||||
break;
|
|
||||||
case obj instanceof Array:
|
|
||||||
child.contentType = "array";
|
|
||||||
child.hasChildren = obj.length > 0;
|
|
||||||
break;
|
|
||||||
case typeof obj === "function":
|
|
||||||
child.contentType = "function";
|
|
||||||
child.hasChildren = true;
|
|
||||||
break;
|
|
||||||
case obj instanceof Object:
|
|
||||||
child.contentType = "object";
|
|
||||||
child.hasChildren = Object.keys(obj).length > 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
child.contentType = typeof obj;
|
|
||||||
child.hasChildren = false;
|
|
||||||
}
|
|
||||||
if (key.type === "set entry") {
|
|
||||||
child.content = this.serializer.serializeItem(obj, true);
|
|
||||||
} else {
|
} else {
|
||||||
child.content = this.serializer.serializeContent(obj, child.contentType);
|
obj = this.toRaw(obj);
|
||||||
|
switch (true) {
|
||||||
|
case obj instanceof Map:
|
||||||
|
child.contentType = "map";
|
||||||
|
child.hasChildren = true;
|
||||||
|
break;
|
||||||
|
case obj instanceof Set:
|
||||||
|
child.contentType = "set";
|
||||||
|
child.hasChildren = true;
|
||||||
|
break;
|
||||||
|
case obj instanceof Array:
|
||||||
|
child.contentType = "array";
|
||||||
|
child.hasChildren = obj.length > 0;
|
||||||
|
break;
|
||||||
|
case typeof obj === "function":
|
||||||
|
child.contentType = "function";
|
||||||
|
child.hasChildren = true;
|
||||||
|
break;
|
||||||
|
case obj instanceof Object:
|
||||||
|
child.contentType = "object";
|
||||||
|
child.hasChildren = Object.keys(obj).length > 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
child.contentType = typeof obj;
|
||||||
|
child.hasChildren = false;
|
||||||
|
}
|
||||||
|
if (key.type === "set entry") {
|
||||||
|
child.content = this.serializer.serializeItem(obj, true);
|
||||||
|
} else {
|
||||||
|
child.content = this.serializer.serializeContent(obj, child.contentType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (child.toggled) {
|
if (child.toggled) {
|
||||||
@@ -881,6 +858,7 @@
|
|||||||
oldTree
|
oldTree
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
this.addHighlightedKeys(child);
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -890,7 +868,10 @@
|
|||||||
let path = completePath.slice(objPathIndex);
|
let path = completePath.slice(objPathIndex);
|
||||||
let obj;
|
let obj;
|
||||||
if (objType === "subscription") {
|
if (objType === "subscription") {
|
||||||
obj = oldTree.subscriptions.children[path[1].value].target;
|
const subscriptionPath = completePath.slice(0, objPathIndex + 3);
|
||||||
|
obj = oldTree.subscriptions.children.find(
|
||||||
|
(child) => JSON.stringify(child.target.path) === JSON.stringify(subscriptionPath)
|
||||||
|
).target;
|
||||||
path = path.slice(3);
|
path = path.slice(3);
|
||||||
} else {
|
} else {
|
||||||
// Everything here is in component if it is not an app so remove this key of the path in the former case
|
// Everything here is in component if it is not an app so remove this key of the path in the former case
|
||||||
@@ -1303,16 +1284,15 @@
|
|||||||
children: [],
|
children: [],
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const rawSubscriptions = node.subscriptions;
|
const rawSubscriptions = this.topLevelSubscriptions(node);
|
||||||
component.subscriptions = {
|
component.subscriptions = {
|
||||||
toggled: oldTree ? oldTree.subscriptions.toggled : true,
|
toggled: oldTree ? oldTree.subscriptions.toggled : true,
|
||||||
children: [],
|
children: [],
|
||||||
};
|
};
|
||||||
rawSubscriptions.forEach((rawSubscription, index) => {
|
rawSubscriptions.forEach((rawSubscription) => {
|
||||||
let subscription = {
|
let subscription = {
|
||||||
keys: [],
|
|
||||||
target: {
|
target: {
|
||||||
name: "target",
|
name: this.targetName(rawSubscription.target, node),
|
||||||
contentType:
|
contentType:
|
||||||
typeof rawSubscription.target === "object"
|
typeof rawSubscription.target === "object"
|
||||||
? Array.isArray(rawSubscription.target)
|
? Array.isArray(rawSubscription.target)
|
||||||
@@ -1323,28 +1303,20 @@
|
|||||||
path: [
|
path: [
|
||||||
...path,
|
...path,
|
||||||
{ type: "item", value: "subscriptions" },
|
{ type: "item", value: "subscriptions" },
|
||||||
{ type: "item", value: index },
|
{ type: "item", value: rawSubscription.index },
|
||||||
{ type: "item", value: "target" },
|
{ type: "item", value: "target" },
|
||||||
],
|
],
|
||||||
toggled: false,
|
toggled: false,
|
||||||
objectType: "subscription",
|
objectType: "subscription",
|
||||||
},
|
},
|
||||||
keysExpanded: false,
|
|
||||||
};
|
};
|
||||||
if (
|
if (
|
||||||
oldTree &&
|
oldTree &&
|
||||||
oldTree.subscriptions.children[index] &&
|
oldTree.subscriptions.children[rawSubscription.index] &&
|
||||||
oldTree.subscriptions.children[index].target.toggled
|
oldTree.subscriptions.children[rawSubscription.index].target.toggled
|
||||||
) {
|
) {
|
||||||
subscription.target.toggled = true;
|
subscription.target.toggled = true;
|
||||||
}
|
}
|
||||||
rawSubscription.keys.forEach((key) => {
|
|
||||||
if (typeof key === "symbol") {
|
|
||||||
subscription.keys.push(key.toString());
|
|
||||||
} else {
|
|
||||||
subscription.keys.push(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (rawSubscription.target == null) {
|
if (rawSubscription.target == null) {
|
||||||
if (subscription.target.contentType === "undefined") {
|
if (subscription.target.contentType === "undefined") {
|
||||||
subscription.target.content = "undefined";
|
subscription.target.content = "undefined";
|
||||||
@@ -1374,6 +1346,7 @@
|
|||||||
oldTree
|
oldTree
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
this.addHighlightedKeys(subscription.target);
|
||||||
component.subscriptions.children.push(subscription);
|
component.subscriptions.children.push(subscription);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1556,7 +1529,7 @@
|
|||||||
}
|
}
|
||||||
// Returns the tree of components of the inspected page in a parsed format
|
// Returns the tree of components of the inspected page in a parsed format
|
||||||
// Use inspectedPath to specify the path of the selected component
|
// Use inspectedPath to specify the path of the selected component
|
||||||
getComponentsTree(inspectedPath = null, oldTrees = null) {
|
getComponentsTree(inspectedPath = null, oldTrees = null, oldDetails = null) {
|
||||||
const appsArray = [...this.apps];
|
const appsArray = [...this.apps];
|
||||||
const trees = appsArray.map((app, index) => {
|
const trees = appsArray.map((app, index) => {
|
||||||
let oldTree;
|
let oldTree;
|
||||||
@@ -1609,7 +1582,8 @@
|
|||||||
}
|
}
|
||||||
return appNode;
|
return appNode;
|
||||||
});
|
});
|
||||||
return trees ? trees : [];
|
const component = this.getComponentDetails(inspectedPath, oldDetails);
|
||||||
|
return trees ? [trees, component] : [];
|
||||||
}
|
}
|
||||||
// Recursively fills the components tree as a parsed version
|
// Recursively fills the components tree as a parsed version
|
||||||
fillTree(appNode, treeNode, inspectedPathString, oldBranch) {
|
fillTree(appNode, treeNode, inspectedPathString, oldBranch) {
|
||||||
@@ -1705,6 +1679,54 @@
|
|||||||
inspect(obj);
|
inspect(obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
targetName(target, node) {
|
||||||
|
// check on component
|
||||||
|
const { component } = node;
|
||||||
|
for (const [key, value] of Object.entries(component)) {
|
||||||
|
if (target === this.toRaw(value)) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check on props
|
||||||
|
for (const [key, value] of Object.entries(component.props)) {
|
||||||
|
if (target === this.toRaw(value)) {
|
||||||
|
return `props.${key}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "[unknown]";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes subscriptions that are a direct child of another subscription:
|
||||||
|
* they will be reachable from the top level by expanding observed keys.
|
||||||
|
*
|
||||||
|
* @param {ComponentNode} node
|
||||||
|
* @returns {{ keys: PropertyKey[], target: unknown}[]} the top level
|
||||||
|
* subscriptions of the node
|
||||||
|
*/
|
||||||
|
topLevelSubscriptions(node) {
|
||||||
|
const subscriptions = node.subscriptions.map((s, index) => ({ ...s, index }));
|
||||||
|
const topLevelValues = new Set(Object.values(node.component).map((o) => this.toRaw(o)));
|
||||||
|
const toOmit = new Set(
|
||||||
|
subscriptions
|
||||||
|
.flatMap(({ keys, target }) => keys.map((k) => this.toRaw(target[k])))
|
||||||
|
.filter((obj) => !topLevelValues.has(obj))
|
||||||
|
);
|
||||||
|
return subscriptions.filter(({ target }) => !toOmit.has(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
addHighlightedKeys(child) {
|
||||||
|
const { path } = child;
|
||||||
|
const subscriptionIndex = path.findIndex((item) => typeof item !== "string");
|
||||||
|
if (path[subscriptionIndex]?.value === "subscriptions") {
|
||||||
|
const node = this.getComponentNode(path.slice(0, subscriptionIndex));
|
||||||
|
// Add observed keys
|
||||||
|
const targetToKeys = new Map(node.subscriptions.map(({ keys, target }) => [target, keys]));
|
||||||
|
const target = this.getObjectProperty(child.path);
|
||||||
|
child.keys = targetToKeys.get(target)?.map((k) => String(k));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkOwlStatus() {
|
function checkOwlStatus() {
|
||||||
@@ -1717,7 +1739,7 @@
|
|||||||
owlStatus = -1;
|
owlStatus = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.postMessage({ source: "owl-devtools", type: "owlStatus", data: owlStatus });
|
window.postMessage({ source: "owl-devtools", type: "owlStatus", data: owlStatus }, "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!window.__OWL_DEVTOOLS__) {
|
if (!window.__OWL_DEVTOOLS__) {
|
||||||
@@ -1739,19 +1761,19 @@
|
|||||||
if (value?.Fiber !== undefined) {
|
if (value?.Fiber !== undefined) {
|
||||||
window.__OWL__DEVTOOLS_GLOBAL_HOOK__ = new OwlDevtoolsGlobalHook();
|
window.__OWL__DEVTOOLS_GLOBAL_HOOK__ = new OwlDevtoolsGlobalHook();
|
||||||
}
|
}
|
||||||
window.top.postMessage({ source: "owl-devtools", type: "FrameReady" });
|
window.top.postMessage({ source: "owl-devtools", type: "FrameReady" }, "/");
|
||||||
checkOwlStatus();
|
checkOwlStatus();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// Do note that the reload message is not sent on the top window so that it is not intercepted when originating
|
// Do note that the reload message is not sent on the top window so that it is not intercepted when originating
|
||||||
// from an iframe
|
// from an iframe
|
||||||
window.postMessage({ source: "owl-devtools", type: "Reload" });
|
window.postMessage({ source: "owl-devtools", type: "Reload" }, "/");
|
||||||
} else if (
|
} else if (
|
||||||
window.__OWL_DEVTOOLS__?.Fiber !== undefined &&
|
window.__OWL_DEVTOOLS__?.Fiber !== undefined &&
|
||||||
!window.__OWL__DEVTOOLS_GLOBAL_HOOK__
|
!window.__OWL__DEVTOOLS_GLOBAL_HOOK__
|
||||||
) {
|
) {
|
||||||
window.__OWL__DEVTOOLS_GLOBAL_HOOK__ = new OwlDevtoolsGlobalHook();
|
window.__OWL__DEVTOOLS_GLOBAL_HOOK__ = new OwlDevtoolsGlobalHook();
|
||||||
window.postMessage({ source: "owl-devtools", type: "Reload" });
|
window.postMessage({ source: "owl-devtools", type: "Reload" }, "/");
|
||||||
}
|
}
|
||||||
// Listener that checks whether owl is available on the page and if it has the right version
|
// Listener that checks whether owl is available on the page and if it has the right version
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
|
|||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
import type * as owl from "../../../src/runtime";
|
||||||
|
import type { JestChrome } from "jest-chrome/types/jest-chrome";
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
owl: typeof owl;
|
||||||
|
browser: any;
|
||||||
|
chrome: JestChrome;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { mount } from "../../../src";
|
||||||
|
import { makeTestFixture } from "../../../tests/helpers";
|
||||||
|
import { DevtoolsWindow } from "../src/devtools_app/devtools_window/devtools_window";
|
||||||
|
|
||||||
|
let fixture = makeTestFixture();
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = makeTestFixture();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("devtools", () => {
|
||||||
|
test("mounting the devtools", async () => {
|
||||||
|
await mount(DevtoolsWindow as any, fixture);
|
||||||
|
expect(fixture.querySelector(".status-message")!.textContent).toContain("There are no apps currently running.")
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"testEnvironment": "jsdom",
|
||||||
|
"roots": [
|
||||||
|
"<rootDir>"
|
||||||
|
],
|
||||||
|
"setupFiles": [
|
||||||
|
"<rootDir>/../../../tests/mocks/mockEventTarget.js",
|
||||||
|
"<rootDir>/setup.ts"
|
||||||
|
],
|
||||||
|
"globals": {
|
||||||
|
"ts-jest": {
|
||||||
|
"tsconfig": "<rootDir>/tsconfig.json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.[jt]s?$": "ts-jest"
|
||||||
|
},
|
||||||
|
"verbose": false,
|
||||||
|
"testRegex": "(/tests/.*(test|spec))\\.ts?$",
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"ts",
|
||||||
|
"tsx",
|
||||||
|
"js",
|
||||||
|
"jsx",
|
||||||
|
"json",
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import * as owl from "../../../src/runtime";
|
||||||
|
window.owl = owl;
|
||||||
|
import { chrome } from "jest-chrome";
|
||||||
|
window.chrome = chrome;
|
||||||
|
|
||||||
|
import { templates } from "../assets/templates";
|
||||||
|
for (const template in templates) {
|
||||||
|
owl.App.registerTemplate(template, templates[template as keyof typeof templates]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
{
|
||||||
|
/**
|
||||||
|
** Commented-out options have their default values.
|
||||||
|
**/
|
||||||
|
"include": [
|
||||||
|
"**/*.ts",
|
||||||
|
],
|
||||||
|
// "exclude": [],
|
||||||
|
// "files": [], // A list of relative or absolute file paths to include.
|
||||||
|
// "extends": "", // A string containing a path to another configuration file to inherit from.
|
||||||
|
// "references": [], // An array of objects `{"path": "./to/dirOrConfig"}` that specifies projects to reference.
|
||||||
|
// "compileOnSave": false, // Signals to the IDE to generate all files for a given tsconfig.json upon saving.
|
||||||
|
"compilerOptions": {
|
||||||
|
// Main options
|
||||||
|
"target": "es2019", // Specify ECMAScript target version: 'es3' (default), 'es5', 'es2015', 'es2016', 'es2017','es2018' or 'esnext'.
|
||||||
|
"module": "es6", // Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'.
|
||||||
|
// "lib": ["esnext", "dom"], // Specify library files to be included in the compilation.
|
||||||
|
"allowJs": true, // Allow javascript files to be compiled.
|
||||||
|
// "checkJs": false, // Report errors in .js files.
|
||||||
|
// "outFile": "./", // Concatenate and emit output to single file.
|
||||||
|
"outDir": "dist", // Redirect output structure to the directory.
|
||||||
|
// "rootDir": "./", // Specify the root directory of input files. Use to control the output directory structure with `--outDir`.
|
||||||
|
// "project": "", // Compile a project given a valid configuration file.
|
||||||
|
// Compilation options
|
||||||
|
// "composite": true, // Enable project compilation
|
||||||
|
// "diagnostics": false, // Show diagnostic information.
|
||||||
|
// "incremental": true, // Enable incremental compilation by reading/writing information from prior compilations to a file on disk.
|
||||||
|
// "isolatedModules": false, // Transpile each file as a separate module (similar to 'ts.transpileModule').
|
||||||
|
// "listEmittedFiles": false, // Print names of generated files part of the compilation.
|
||||||
|
// "listFiles": true, // Print names of files part of the compilation.
|
||||||
|
// "noErrorTruncation": false, // Do not truncate error messages.
|
||||||
|
// "preserveWatchOutput": false, // Keep outdated console output in watch mode instead of clearing the screen.
|
||||||
|
// "traceResolution": false, // Enable tracing of the name resolution process.
|
||||||
|
// "tsBuildInfoFile": ".tsbuildinfo", // Specify file to store incremental compilation information.
|
||||||
|
// Strict typechecking options
|
||||||
|
"strict": true, // Enable all strict type-checking options.
|
||||||
|
// "noImplicitAny": true, // Raise error on expressions and declarations with an implied 'any' type.
|
||||||
|
// "noImplicitThis": true, // Raise error on 'this' expressions with an implied 'any' type.
|
||||||
|
// "strictBindCallApply": true, // Enable stricter checking of of the `bind`, `call`, and `apply` methods on functions.
|
||||||
|
// "strictFunctionTypes": true, // Disable bivariant parameter checking for function types.
|
||||||
|
// "strictNullChecks": true, // In strict null checking mode, the null and undefined values are not in the domain of every type and are only assignable to themselves and any.
|
||||||
|
// "strictPropertyInitialization": true, // Ensure non-undefined class properties are initialized in the constructor. This option requires `--strictNullChecks` be enabled in order to take effect.
|
||||||
|
// "alwaysStrict": true, // Parse in strict mode and emit "use strict" for each source file.
|
||||||
|
// Additional checks
|
||||||
|
// "allowUnreachableCode": false, // Do not report errors on unreachable code.
|
||||||
|
// "allowUnusedLabels": false, // Do not report errors on unused labels.
|
||||||
|
"forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file.
|
||||||
|
// "noStrictGenericChecks": false, // Disable strict checking of generic signatures in function types.
|
||||||
|
"noUnusedLocals": true, // Report errors on unused locals.
|
||||||
|
"noUnusedParameters": false, // Report errors on unused parameters.
|
||||||
|
"noImplicitReturns": true, // Report error when not all code paths in function return a value.
|
||||||
|
"noFallthroughCasesInSwitch": true, // Report errors for fallthrough cases in switch statement.
|
||||||
|
// "skipLibCheck": false, // Skip type checking of all declaration files (*.d.ts).
|
||||||
|
// "suppressExcessPropertyErrors": false, // Suppress excess property checks for object literals.
|
||||||
|
// "suppressImplicitAnyIndexErrors": false, // Suppress noImplicitAny errors for indexing objects lacking index signatures.
|
||||||
|
// Module resolution options
|
||||||
|
"moduleResolution": "node", // Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6).
|
||||||
|
// "baseUrl": "./", // Base directory to resolve non-absolute module names.
|
||||||
|
// "paths": {}, // A series of entries which re-map imports to lookup locations relative to the 'baseUrl'.
|
||||||
|
// "rootDirs": [], // List of root folders whose combined content represents the structure of the project at runtime.
|
||||||
|
// "typeRoots": [
|
||||||
|
// "./node_modules/@types",
|
||||||
|
// "./tools/devtools/tests"
|
||||||
|
// ], // List of folders to include type definitions from.
|
||||||
|
"types": [
|
||||||
|
"jest",
|
||||||
|
"node"
|
||||||
|
], // Type declaration files to be included in compilation.
|
||||||
|
// "allowSyntheticDefaultImports": false // Allow default imports from modules with no default export. This does not affect code emit, just typechecking.
|
||||||
|
"esModuleInterop": true, // Emit '__importStar' and '__importDefault' helpers for runtime babel ecosystem compatibility and enable '--allowSyntheticDefaultImports' for typesystem compatibility.
|
||||||
|
// "maxNodeModuleJsDepth": 0, // The maximum dependency depth to search under node_modules and load JavaScript files. Only applicable with --allowJs.
|
||||||
|
// "preserveSymlinks": false, // Do not resolve the real path of symlinks.
|
||||||
|
"resolveJsonModule": true, // Include modules imported with '.json' extension.
|
||||||
|
// Emit options
|
||||||
|
"declaration": true, // Generates corresponding '.d.ts' file.
|
||||||
|
"declarationDir": "dist/types", // Output directory for generated declaration files.
|
||||||
|
// "declarationMap": false, // Generates a sourcemap for each corresponding '.d.ts' file.
|
||||||
|
// "emitBOM": false, // Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files.
|
||||||
|
// "emitDeclarationOnly": false, // Only emit ‘.d.ts’ declaration files.
|
||||||
|
// "importHelpers": false, // Import emit helpers from 'tslib'.
|
||||||
|
// "newLine": "LF", // Use the specified end of line sequence to be used when emitting files: "crlf" (windows) or "lf" (unix).”
|
||||||
|
// "noEmit": true, // Do not emit outputs.
|
||||||
|
// "noEmitHelpers": false, // Do not generate custom helper functions like __extends in compiled output.
|
||||||
|
// "noEmitOnError": false, // Do not emit outputs if any errors were reported.
|
||||||
|
// "noImplicitUseStrict": false, // Do not emit "use strict" directives in module output.
|
||||||
|
// "noResolve": false, // Do not add triple-slash references or module import targets to the list of compiled files.
|
||||||
|
"preserveConstEnums": false, // Do not erase const enum declarations in generated code.
|
||||||
|
// "removeComments": false, // Remove all comments except copy-right header comments beginning with
|
||||||
|
// "experimentalDecorators": true, // Enables experimental support for ES7 decorators.
|
||||||
|
// "emitDecoratorMetadata": true, // Enables experimental support for emitting type metadata for decorators.
|
||||||
|
// Source map options
|
||||||
|
// "sourceMap": false, // Generates corresponding '.map' file.
|
||||||
|
// "sourceRoot": "", // Specify the location where debugger should locate TypeScript files instead of source locations.
|
||||||
|
// "mapRoot": "", // Specify the location where debugger should locate map files instead of generated locations.
|
||||||
|
// "inlineSourceMap": true, // Emit a single file with source maps instead of having a separate file.
|
||||||
|
// "inlineSources": true, // Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set.
|
||||||
|
// JSX options
|
||||||
|
// "jsx": "preserve", // Specify JSX code generation: 'preserve', 'react-native', or 'react'.
|
||||||
|
// "jsxFactory": "React.createElement", // Specify the JSX factory function to use when targeting react JSX emit, e.g. 'React.createElement' or 'h'.
|
||||||
|
// Other options
|
||||||
|
// "allowUmdGlobalAccess": true, // Allow accessing UMD globals from modules.
|
||||||
|
// "charset": "utf8", // The character set of the input files.
|
||||||
|
// "downlevelIteration": false, // Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'.
|
||||||
|
// "disableSizeLimit": false, // Disable size limitation on JavaScript project.
|
||||||
|
// "keyofStringsOnly": false, // Resolve 'keyof' to string valued property names only (no numbers or symbols).
|
||||||
|
// "noLib": false, // Do not include the default library file (lib.d.ts).
|
||||||
|
// "pretty": true, // Stylize errors and messages using color and context.
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user