apostrophe
Version:
The Apostrophe Content Management System.
445 lines (422 loc) • 12.8 kB
JavaScript
const assert = require('node:assert/strict');
const path = require('node:path');
const fs = require('fs-extra');
const t = require('../test-lib/test.js');
describe('Asset - External Build', function () {
let apos;
const publicPath = path.join(process.cwd(), 'test/public');
const publicBuildPath = path.join(publicPath, 'apos-frontend');
this.timeout(t.timeout);
after(async function () {
await fs.remove(publicBuildPath);
await t.destroy(apos);
});
beforeEach(async function () {
await fs.remove(publicBuildPath);
});
it('should register external build module', async function () {
let actualInit;
let actualBuilt = false;
let actualDevServer;
apos = await t.create({
root: module,
autoBuild: true,
modules: {
'asset-vite': {
before: '@apostrophecms/asset',
init(self) {
actualInit = true;
},
handlers(self) {
return {
'@apostrophecms/asset:afterInit': {
async registerExternalBuild() {
self.apos.asset.configureBuildModule(self, {
alias: 'vite',
devServer: true,
hmr: true
});
}
}
};
},
methods(self) {
return {
async build() {
actualBuilt = true;
return {
entrypoints: []
};
},
async watch() { },
async startDevServer() {
actualDevServer = true;
return {
entrypoints: []
};
},
async entrypoints() {
return [];
}
};
}
}
}
});
assert.ok(apos.asset.buildWatcher);
assert.equal(actualInit, true);
assert.equal(actualBuilt, false);
assert.equal(actualDevServer, true);
assert.equal(apos.asset.hasBuildModule(), true);
assert.equal(apos.asset.getBuildModuleAlias(), 'vite');
assert.deepEqual(apos.asset.getBuildModuleConfig(), {
name: 'asset-vite',
alias: 'vite',
devServer: true,
hmr: true
});
assert.equal(apos.asset.getBuildModule().__meta.name, 'asset-vite');
// Webpack build wasn't triggered
assert.throws(() => fs.readdirSync(path.join(publicBuildPath, 'default/apos-module-bundle.js')), {
code: 'ENOENT'
});
});
it('should support `build` configuration per module (BC)', async function () {
await t.destroy(apos);
apos = await t.create({
root: module,
modules: {
'@apostrophecms/asset': {
options: {
rebundleModules: {
'article-page': 'article',
'article-widget': 'main',
'selected-article-widget:tabs': 'tools',
'@apostrophecms/my-home-page:main': 'main'
}
}
},
webpack: {
before: '@apostrophecms/asset',
init() {},
handlers(self) {
return {
'@apostrophecms/asset:afterInit': {
async registerExternalBuild() {
self.apos.asset.configureBuildModule(self, {
alias: 'webpack',
hasDevServer: true,
hasHMR: true
});
}
}
};
},
methods(self) {
return {
async build() { },
async watch() { },
async startDevServer() {},
async entrypoints() {
return [];
}
};
}
},
'@apostrophecms/home-page': {
webpack: {
bundles: {
topic: {},
main: {}
}
},
build: {
webpack: {
bundles: {
topic: {},
main: {}
}
}
}
},
article: {
extend: '@apostrophecms/piece-type',
init() {}
},
'article-page': {
webpack: {
bundles: {
main: {}
},
extensions: {
topic: {
resolve: {
alias: {
Utils: path.join(process.cwd(), 'lib/utils/')
}
}
},
ext1 ({ mode, alias = {} }) {
return {
mode,
resolve: {
alias: {
ext1: 'ext1-path',
...alias
}
}
};
}
},
extensionOptions: {
ext1: {
mode: 'production'
}
}
},
build: {
webpack: {
bundles: {
main: {}
},
// Here only to demonstrate that it's BC,
// this is ignored by the new Vite build module
extensions: {
topic: {
resolve: {
alias: {
Utils: path.join(process.cwd(), 'lib/utils/')
}
}
},
ext1 ({ mode, alias = {} }) {
return {
mode,
resolve: {
alias: {
ext1: 'ext1-path',
...alias
}
}
};
}
},
extensionOptions: {
ext1: {
mode: 'production'
}
}
}
}
},
'article-widget': {
webpack: {
bundles: {
topic: {},
carousel: {}
}
},
build: {
webpack: {
bundles: {
topic: {},
carousel: {}
}
}
}
},
'selected-article-widget': {
webpack: {
bundles: {
tabs: {}
}
},
build: {
webpack: {
bundles: {
tabs: {}
}
}
}
}
}
});
const actual = apos.asset.moduleBuildExtensions.webpack;
const expected = {};
await apos.asset.setWebpackExtensions(expected);
assert.deepEqual(
actual,
expected,
'`build` configuration is not identical to the legacy `webpack` configuration'
);
});
it('should inject custom bundles dynamic CSS (PRO-6904)', async function () {
await t.destroy(apos);
apos = await t.create({
root: module,
autoBuild: true,
modules: {
'asset-vite': {
before: '@apostrophecms/asset',
handlers(self) {
return {
'@apostrophecms/asset:afterInit': {
async registerExternalBuild() {
self.apos.asset.configureBuildModule(self, {
alias: 'vite',
devServer: false,
hmr: false
});
}
}
};
},
methods(self) {
return {
async build() {
return {
entrypoints: []
};
},
async watch() { },
async startDevServer() {
return {
entrypoints: []
};
},
async entrypoints() {
return [];
}
};
}
}
}
});
// Mock the build manifest
apos.asset.currentBuildManifest = {
sourceMapsRoot: null,
devServerUrl: null,
hmrTypes: [],
entrypoints: [
{
name: 'src',
type: 'index',
scenes: [ 'apos', 'public' ],
outputs: [ 'css', 'js' ],
condition: 'module',
manifest: {
root: 'dist',
name: 'src',
devServer: false
},
bundles: [
'apos-src-module-bundle.js',
'apos-bundle.css',
'public-src-module-bundle.js',
'public-bundle.css'
]
},
{
name: 'counter-react',
type: 'custom',
scenes: [ 'counter-react' ],
outputs: [ 'css', 'js' ],
condition: 'module',
prologue: '',
ignoreSources: [],
manifest: {
root: 'dist',
name: 'counter-react',
devServer: false
},
bundles: [ 'counter-react-module-bundle.js' ]
},
{
name: 'counter-svelte',
type: 'custom',
scenes: [ 'counter-svelte' ],
outputs: [ 'css', 'js' ],
condition: 'module',
manifest: {
root: 'dist',
name: 'counter-svelte',
devServer: false
},
bundles: [
'counter-svelte-module-bundle.js',
'counter-svelte-bundle.css'
]
},
{
name: 'counter-vue',
type: 'custom',
scenes: [ 'counter-vue' ],
outputs: [ 'css', 'js' ],
condition: 'module',
manifest: {
root: 'dist',
name: 'counter-vue',
devServer: false
},
bundles: [ 'counter-vue-module-bundle.js', 'counter-vue-bundle.css' ]
},
{
name: 'apos',
type: 'apos',
scenes: [ 'apos' ],
outputs: [ 'js' ],
condition: 'module',
manifest: {
root: 'dist',
name: 'apos',
devServer: false
},
bundles: [ 'apos-module-bundle.js', 'apos-bundle.css' ]
}
]
};
// `counter-svelte` has `ui/src/counter-svelte.scss`.
// `counter-vue` doesn't have any CSS but has dynamic CSS, extracted
// from Vue components.
apos.asset.extraBundles = {
js: [ 'counter-react', 'counter-svelte', 'counter-vue' ],
css: [ 'counter-svelte ' ]
};
apos.asset.rebundleModules = [];
const payload = {
page: {
type: '@apostrophecms/home-page'
},
scene: 'apos',
template: '@apostrophecms/home-page:page',
content: '[stylesheets-placeholder:1]\n[scripts-placeholder:1]',
scriptsPlaceholder: '[scripts-placeholder:1]',
stylesheetsPlaceholder: '[stylesheets-placeholder:1]',
widgetsBundles: { 'counter-vue': {} }
};
const injected = apos.template.insertBundlesMarkup(payload);
// All bundles are injected, including the dynamic CSS from `counter-vue`
const actual = injected.replace(/>\s+</g, '><');
const expected = '<link rel="stylesheet" href="/apos-frontend/default/apos-bundle.css">' +
'<link rel="stylesheet" href="/apos-frontend/default/counter-svelte-bundle.css">' +
'<link rel="stylesheet" href="/apos-frontend/default/counter-vue-bundle.css">' +
'<script type="module" src="/apos-frontend/default/apos-src-module-bundle.js"></script>' +
'<script type="module" src="/apos-frontend/default/apos-module-bundle.js"></script>' +
'<script type="module" src="/apos-frontend/default/counter-react-module-bundle.js"></script>' +
'<script type="module" src="/apos-frontend/default/counter-svelte-module-bundle.js"></script>' +
'<script type="module" src="/apos-frontend/default/counter-vue-module-bundle.js"></script>';
assert.equal(actual, expected, 'Bundles are not injected correctly');
const injectedPublic = apos.template.insertBundlesMarkup({
...payload,
scene: 'public'
});
// Showcases that the only the dynamic Vue CSS is injected, because the
// widget owning the bundle counter-vue is present.
const actualPublic = injectedPublic.replace(/>\s+</g, '><');
const expectedPublic = '<link rel="stylesheet" href="/apos-frontend/default/public-bundle.css">' +
'<link rel="stylesheet" href="/apos-frontend/default/counter-vue-bundle.css">' +
'<script type="module" src="/apos-frontend/default/public-src-module-bundle.js"></script>' +
'<script type="module" src="/apos-frontend/default/counter-vue-module-bundle.js"></script>';
assert.equal(actualPublic, expectedPublic, 'Bundles are not injected correctly');
});
});