angular - Webpack如何像Angular一样在装饰器中导入html模板
问题描述
我正在为自定义元素编写一个装饰器,主要代码是:
interface CustomElementOptions {
selector: string,
template?: { default: string };
}
function CustomElement(options: CustomElementOptions) {
return (target: CustomElementConstructor) => {
if(options.template) {
const template = document.createElement('template');
template.innerHTML = options.template.default;
Object.defineProperty(target.prototype, 'template', {
value: template,
});
}
window.customElements.define(options.selector, target as any);
};
}
@CustomElement({
selector: 'test-span',
template: require('./template.html'), // <---------------------- I want to change here with `templateUrl`
})
export class SpanElement extends HTMLElement {
public template?: HTMLTemplateElement;
constructor() {
super();
if(this.template) {
this.appendChild(this.template.content.cloneNode(true));
}
}
}
我现在raw-loader
用来加载 html 模板,但这还不够优雅,我想像 Angular:
@CustomElement({
selector: 'test-span',
templateUrl: './template.html', // <---------------------- templateUrl
})
export class SpanElement extends HTMLElement { ... }
但我不能在装饰器中使用 require 或 import ,错误消息如下:
Uncaught Error: Cannot find module './template.html'
at webpackEmptyContext (app|sync:2)
at application.ts:10
at __webpack_modules__../src/app/application.ts.__decorate (index.js:6)
at Object../src/app/application.ts (application.ts:20)
at __webpack_require__ (bootstrap:18)
at Object../src/main.ts (main.ts:1)
at __webpack_require__ (bootstrap:18)
at startup:3
at startup:5
angular-cli 里面有什么魔力?我尽力了,谢谢你的帮助!
解决方案
终于,终于!我找到了解决方案。
简短的回答是更新打字稿 ast。
接下来是我的详细步骤。
一开始我不知道,我想实现自动化require('./template.html')
,然后我想到了AST,我ts-loader
用于编译打字稿,ts-loader
提供getCustomTransformers
自定义打字稿转换器,这是我的转换器,请忽略我乱码,这是我第一次工作使用 AST:
{
loader: 'ts-loader',
options: {
configFile: 'tsconfig.json',
getCustomTransformers: program => {
return {
before: [
context => {
const isIncludeTemplate = node => {
return !!node.arguments[0].properties.find(property => property.name.escapedText === 'template');
};
const updateCustomElement = node => {
if(isIncludeTemplate(node)) {
return node;
} else {
const objectLiteralExpression = node.arguments[0];
objectLiteralExpression.properties.push(
factory.createPropertyAssignment(
factory.createIdentifier('template'),
factory.createPropertyAccessExpression(
factory.createCallExpression(
factory.createIdentifier('require'),
undefined,
[objectLiteralExpression.properties.find(property => property.name.escapedText === 'templateUrl').initializer]
),
factory.createIdentifier('default')
),
),
);
return factory.updateCallExpression(node, node.expression, undefined, [objectLiteralExpression]);
}
};
const visit = node => {
if(ts.isCallExpression(node) && node.expression.escapedText === 'CustomElement') {
const newNode = updateCustomElement(node);
return newNode;
}
return ts.visitEachChild(node, child => visit(child), context);
};
return node => ts.visitNode(node, visit);
},
],
};
},
},
}
推荐阅读
- python - 赋值前引用的局部变量“i”
- spring-boot - 无法确定 org.springframework.boot:spring-boot-starter-parent 的工件:
- android - 如何通过 Google Play 跟踪应用程序网站上的按钮点击到 Android 应用程序中的应用程序内购买?
- php - Laravel 事件未显示在 Websockets 仪表板上
- azure - 在 ADF 上将列拆分为行
- php - 致命错误:未捕获的错误:C:\xampp\htdocs\imagebob\google-drive.php:41 中未定义的常量“STDIN”
- python - 将 XML 转换为没有根的 JSON
- python - 试图理解打印 python 中的 Flush 参数行为
- networking - 本地网络的 CIDR 范围是什么意思,我们如何检查它?
- python - 硒蟒 | 输入输入