使用 rollup.js 构建 Angular 模块:外部 html 模板文件将不起作用 (404)
Build Angular module with rollup.js: external html template file won't work (404)
我正在开发一个Angular库(GitHub repo link),有
- lib 模块源位于
./src
- 测试应用程序源位于
./app
- lib 分布在
./dist
构建过程使用 rollup.js 并基于 angular-library-starter。
我还有一个从 ./dist
生成 npm 包并将其安装到 ./node_modules
的过程。问题是应用程序与从 ./src
导入的 lib 模块一起工作正常,当我从 ./dist
或 ./node_modules
:
导入它时不起作用
// ./app/app/app.module.ts
import { AppComponent } from './app.component';
import { UiScrollModule } from '../../src/ngx-ui-scroll'; // works
//import { UiScrollModule } from 'ngx-ui-scroll'; // doesn't work
//import { UiScrollModule } from '../../dist/bundles/ngx-ui-scroll.umd.js'; // no...
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, UiScrollModule],
bootstrap: [AppComponent]
})
export class AppModule { }
构建过程中没有错误,但浏览器控制台显示(对于 ./dist
和 ./node_modules
导入案例):
GET http://localhost:4200/ui-scroll.component.html 404 (Not Found)
Failed to load ui-scroll.component.html
而且我的 lib 模块确实有一个具有外部模板的组件:
// ./src/component/ui-scroll.component.ts
@Component({
selector: 'app-ui-scroll',
templateUrl: './ui-scroll.component.html'
})
export class UiScrollComponent implements OnInit, OnDestroy { /* ... */ }
如果我将内联模板(在 @Component
设置中使用 template
而不是 templateUrl
),它会起作用,我已经测试过了。但我希望模板位于单独的文件中......我相信这是构建过程的责任。有一堆与我的构建过程相关的配置,我认为在这里列出它们并不是一个好主意。它们可以在 lib repository 找到(或者我可以 post 根据要求提供任何确切的零件)。
有没有人遇到过外部 html 模板 rollup-bundling 的问题?
I believe this is a build process responsibility.
看来你是对的。我们通常在构建过程中内联模板。
为此,您可以创建如下 js 文件:
/utils/inline-resouces.js
const {dirname, join} = require('path');
const {readFileSync, writeFileSync} = require('fs');
const glob = require('glob');
/** Finds all JavaScript files in a directory and inlines all resources of Angular components. */
module.exports = function inlineResourcesForDirectory(folderPath) {
glob.sync(join(folderPath, '**/*.js')).forEach(filePath => inlineResources(filePath));
};
/** Inlines the external resources of Angular components of a file. */
function inlineResources(filePath) {
let fileContent = readFileSync(filePath, 'utf-8');
fileContent = inlineTemplate(fileContent, filePath);
fileContent = inlineStyles(fileContent, filePath);
writeFileSync(filePath, fileContent, 'utf-8');
}
/** Inlines the templates of Angular components for a specified source file. */
function inlineTemplate(fileContent, filePath) {
return fileContent.replace(/templateUrl:\s*'([^']+?\.html)'/g, (_match, templateUrl) => {
const templatePath = join(dirname(filePath), templateUrl);
const templateContent = loadResourceFile(templatePath);
return `template: "${templateContent}"`;
});
}
/** Inlines the external styles of Angular components for a specified source file. */
function inlineStyles(fileContent, filePath) {
return fileContent.replace(/styleUrls:\s*(\[[\s\S]*?])/gm, (_match, styleUrlsValue) => {
// The RegExp matches the array of external style files. This is a string right now and
// can to be parsed using the `eval` method. The value looks like "['AAA.css', 'BBB.css']"
const styleUrls = eval(styleUrlsValue);
const styleContents = styleUrls
.map(url => join(dirname(filePath), url))
.map(path => loadResourceFile(path));
return `styles: ["${styleContents.join(' ')}"]`;
});
}
/** Loads the specified resource file and drops line-breaks of the content. */
function loadResourceFile(filePath) {
return readFileSync(
filePath.replace('dist\package\esm5\', '').replace('dist\', ''), 'utf-8')
.replace(/([\n\r]\s*)+/gm, ' ')
.replace(/"/g, '\"');
}
然后按如下方式更改您的 build.js
文件:
build.js
...
const ESM5_DIR = `${NPM_DIR}/esm5`;
const BUNDLES_DIR = `${NPM_DIR}/bundles`;
const OUT_DIR_ESM5 = `${NPM_DIR}/package/esm5`;
// 1) import function from created above file
const inlineResourcesForDirectory = require('./utils/inline-resources');
// 1) end
...
/* AoT compilation */
shell.echo(`Start AoT compilation`);
if (shell.exec(`ngc -p tsconfig-build.json`).code !== 0) {
shell.echo(chalk.red(`Error: AoT compilation failed`));
shell.exit(1);
}
shell.echo(chalk.green(`AoT compilation completed`));
// 2) Inline template after first ngc build
shell.echo(`Start inlining templates in ${NPM_DIR} folder`);
inlineResourcesForDirectory(NPM_DIR);
shell.echo(`Inlining templates in ${NPM_DIR} folder completed`);
// 2) end
...
shell.echo(`Produce ESM5 version`);
shell.exec(`ngc -p tsconfig-build.json --target es5 -d false --outDir ${OUT_DIR_ESM5} --importHelpers true --sourceMap`);
// 3) Inline template after second ngc build
shell.echo(`Start inlining templates in ${OUT_DIR_ESM5} folder`);
inlineResourcesForDirectory(OUT_DIR_ESM5);
shell.echo(`Inlining templates in ${OUT_DIR_ESM5} folder completed`);
// 3) end
if (shell.exec(`rollup -c rollup.es.config.js -i ${OUT_DIR_ESM5}/${PACKAGE}.js -o ${ESM5_DIR}/${PACKAGE}.js`).code !== 0) {
完成上述所有 3 项更改后,您可以检查 ngx-ui-scroll.umd.js
捆绑包。它应该看起来像:
另见
我正在开发一个Angular库(GitHub repo link),有
- lib 模块源位于
./src
- 测试应用程序源位于
./app
- lib 分布在
./dist
构建过程使用 rollup.js 并基于 angular-library-starter。
我还有一个从 ./dist
生成 npm 包并将其安装到 ./node_modules
的过程。问题是应用程序与从 ./src
导入的 lib 模块一起工作正常,当我从 ./dist
或 ./node_modules
:
// ./app/app/app.module.ts
import { AppComponent } from './app.component';
import { UiScrollModule } from '../../src/ngx-ui-scroll'; // works
//import { UiScrollModule } from 'ngx-ui-scroll'; // doesn't work
//import { UiScrollModule } from '../../dist/bundles/ngx-ui-scroll.umd.js'; // no...
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, UiScrollModule],
bootstrap: [AppComponent]
})
export class AppModule { }
构建过程中没有错误,但浏览器控制台显示(对于 ./dist
和 ./node_modules
导入案例):
GET http://localhost:4200/ui-scroll.component.html 404 (Not Found)
Failed to load ui-scroll.component.html
而且我的 lib 模块确实有一个具有外部模板的组件:
// ./src/component/ui-scroll.component.ts
@Component({
selector: 'app-ui-scroll',
templateUrl: './ui-scroll.component.html'
})
export class UiScrollComponent implements OnInit, OnDestroy { /* ... */ }
如果我将内联模板(在 @Component
设置中使用 template
而不是 templateUrl
),它会起作用,我已经测试过了。但我希望模板位于单独的文件中......我相信这是构建过程的责任。有一堆与我的构建过程相关的配置,我认为在这里列出它们并不是一个好主意。它们可以在 lib repository 找到(或者我可以 post 根据要求提供任何确切的零件)。
有没有人遇到过外部 html 模板 rollup-bundling 的问题?
I believe this is a build process responsibility.
看来你是对的。我们通常在构建过程中内联模板。
为此,您可以创建如下 js 文件:
/utils/inline-resouces.js
const {dirname, join} = require('path');
const {readFileSync, writeFileSync} = require('fs');
const glob = require('glob');
/** Finds all JavaScript files in a directory and inlines all resources of Angular components. */
module.exports = function inlineResourcesForDirectory(folderPath) {
glob.sync(join(folderPath, '**/*.js')).forEach(filePath => inlineResources(filePath));
};
/** Inlines the external resources of Angular components of a file. */
function inlineResources(filePath) {
let fileContent = readFileSync(filePath, 'utf-8');
fileContent = inlineTemplate(fileContent, filePath);
fileContent = inlineStyles(fileContent, filePath);
writeFileSync(filePath, fileContent, 'utf-8');
}
/** Inlines the templates of Angular components for a specified source file. */
function inlineTemplate(fileContent, filePath) {
return fileContent.replace(/templateUrl:\s*'([^']+?\.html)'/g, (_match, templateUrl) => {
const templatePath = join(dirname(filePath), templateUrl);
const templateContent = loadResourceFile(templatePath);
return `template: "${templateContent}"`;
});
}
/** Inlines the external styles of Angular components for a specified source file. */
function inlineStyles(fileContent, filePath) {
return fileContent.replace(/styleUrls:\s*(\[[\s\S]*?])/gm, (_match, styleUrlsValue) => {
// The RegExp matches the array of external style files. This is a string right now and
// can to be parsed using the `eval` method. The value looks like "['AAA.css', 'BBB.css']"
const styleUrls = eval(styleUrlsValue);
const styleContents = styleUrls
.map(url => join(dirname(filePath), url))
.map(path => loadResourceFile(path));
return `styles: ["${styleContents.join(' ')}"]`;
});
}
/** Loads the specified resource file and drops line-breaks of the content. */
function loadResourceFile(filePath) {
return readFileSync(
filePath.replace('dist\package\esm5\', '').replace('dist\', ''), 'utf-8')
.replace(/([\n\r]\s*)+/gm, ' ')
.replace(/"/g, '\"');
}
然后按如下方式更改您的 build.js
文件:
build.js
...
const ESM5_DIR = `${NPM_DIR}/esm5`;
const BUNDLES_DIR = `${NPM_DIR}/bundles`;
const OUT_DIR_ESM5 = `${NPM_DIR}/package/esm5`;
// 1) import function from created above file
const inlineResourcesForDirectory = require('./utils/inline-resources');
// 1) end
...
/* AoT compilation */
shell.echo(`Start AoT compilation`);
if (shell.exec(`ngc -p tsconfig-build.json`).code !== 0) {
shell.echo(chalk.red(`Error: AoT compilation failed`));
shell.exit(1);
}
shell.echo(chalk.green(`AoT compilation completed`));
// 2) Inline template after first ngc build
shell.echo(`Start inlining templates in ${NPM_DIR} folder`);
inlineResourcesForDirectory(NPM_DIR);
shell.echo(`Inlining templates in ${NPM_DIR} folder completed`);
// 2) end
...
shell.echo(`Produce ESM5 version`);
shell.exec(`ngc -p tsconfig-build.json --target es5 -d false --outDir ${OUT_DIR_ESM5} --importHelpers true --sourceMap`);
// 3) Inline template after second ngc build
shell.echo(`Start inlining templates in ${OUT_DIR_ESM5} folder`);
inlineResourcesForDirectory(OUT_DIR_ESM5);
shell.echo(`Inlining templates in ${OUT_DIR_ESM5} folder completed`);
// 3) end
if (shell.exec(`rollup -c rollup.es.config.js -i ${OUT_DIR_ESM5}/${PACKAGE}.js -o ${ESM5_DIR}/${PACKAGE}.js`).code !== 0) {
完成上述所有 3 项更改后,您可以检查 ngx-ui-scroll.umd.js
捆绑包。它应该看起来像:
另见