Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/visual-tests-demos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,6 @@ jobs:
devextreme-react-installer.tgz
devextreme-vue-installer.tgz
retention-days: 1

build-vendor-bundles:
name: Build vendor bundles (React, Vue)
runs-on: devextreme-shr2
Expand Down
5 changes: 1 addition & 4 deletions apps/demos/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,9 @@ shared/empty-file.js
Demos/**/config.js
Demos/**/tsconfig.json

# esbuild in-place demo build output (see utils/build/) — generated next to
# each demo's own source, same convention as Demos/**/config.js above.
Demos/**/bundle.js
Demos/**/bundle.css
# esbuild's own code-splitting output for batched Angular demo builds (see
# csp-bundle-angular.js's makeBuildOptions) — shared chunks, not owned by any one demo.
Demos/**/demo.manifest.json
Demos/_chunks

.DS_Store
Expand Down
1 change: 0 additions & 1 deletion apps/demos/utils/server/build-vendor-bundles.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// CLI: node utils/server/build-vendor-bundles.js [React|Vue ...]
// Defaults to both. Angular isn't supported here — see csp-bundle-angular.js's
// `splitting: true` batch build instead.

const path = require('path');
const fs = require('fs');
Expand Down
43 changes: 23 additions & 20 deletions apps/demos/utils/server/csp-bundle-angular.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ const DEMOS_APP_ROOT = path.resolve(__dirname, '..', '..');
const REPO_ROOT = path.resolve(DEMOS_APP_ROOT, '..', '..');
const SRC_DEMOS_DIR = path.join(DEMOS_APP_ROOT, 'Demos');

const IN_PLACE = process.env.BUNDLE_IN_PLACE === '1';
const OUT_ROOT = IN_PLACE ? SRC_DEMOS_DIR : path.join(DEMOS_APP_ROOT, 'csp-bundled-demos');
const IS_GENERATE_MANIFESTS = process.env.CSP_BUNDLE_GENERATE_MANIFESTS === '1';
const NODE_MODULES = path.join(DEMOS_APP_ROOT, 'node_modules');
const FRAMEWORK = 'Angular';

Expand Down Expand Up @@ -524,6 +523,19 @@ function prepareDemo(demo) {
return { ...demo, effectiveEntry, fileReplacements };
}

const ANGULAR_IMPLICIT_PACKAGES = ['zone.js', 'reflect-metadata', 'devextreme-dist'];
function writeDemoManifest(demo, destDir) {
const { discoverDemoSpecifiers, resolvePackageVersions } = require('./vendor-bundle');
const packages = resolvePackageVersions([
...discoverDemoSpecifiers(demo.srcDir),
...ANGULAR_IMPLICIT_PACKAGES,
]);
fs.writeFileSync(
path.join(destDir, 'demo.manifest.json'),
JSON.stringify({ framework: FRAMEWORK, packages }, null, 2),
);
}

function sortJsFiles(jsFiles) {
return jsFiles.sort((a, b) => {
if (a === 'polyfills.js') return -1;
Expand Down Expand Up @@ -606,7 +618,7 @@ function makeBuildOptions({

async function bundleDemo(demo, createCompilerPlugin, destDirOverride) {
const prepared = demo.effectiveEntry ? demo : prepareDemo(demo);
const destDir = destDirOverride || path.join(OUT_ROOT, prepared.widget, prepared.name, FRAMEWORK);
const destDir = destDirOverride || path.join(SRC_DEMOS_DIR, prepared.widget, prepared.name, FRAMEWORK);
fs.mkdirSync(destDir, { recursive: true });

const effectiveEntry = prepared.effectiveEntry;
Expand Down Expand Up @@ -638,6 +650,7 @@ async function bundleDemo(demo, createCompilerPlugin, destDirOverride) {
const cssFiles = outputs.filter((o) => o.endsWith('.css')).map((o) => path.basename(o));

fs.writeFileSync(path.join(destDir, 'index.html'), buildHtml({ jsFiles, cssFiles, srcDir: prepared.srcDir }));
if (IS_GENERATE_MANIFESTS) writeDemoManifest(prepared, destDir);
return { ok: true };
}

Expand All @@ -654,7 +667,7 @@ async function bundleDemoBatch(batch, createCompilerPlugin) {
try {
await esbuild.build(makeBuildOptions({
entryPoints,
outdir: OUT_ROOT,
outdir: SRC_DEMOS_DIR,
tsconfig,
fileReplacements,
createCompilerPlugin,
Expand All @@ -664,7 +677,7 @@ async function bundleDemoBatch(batch, createCompilerPlugin) {
}

for (const demo of batch) {
const destDir = path.join(OUT_ROOT, demo.widget, demo.name, FRAMEWORK);
const destDir = path.join(SRC_DEMOS_DIR, demo.widget, demo.name, FRAMEWORK);
const cssFiles = ['bundle.css'].filter((file) => fs.existsSync(path.join(destDir, file)));
const localJsFiles = sortJsFiles(['bundle.js'].filter((file) => fs.existsSync(path.join(destDir, file))));
if (localJsFiles.length === 0) {
Expand All @@ -676,6 +689,7 @@ async function bundleDemoBatch(batch, createCompilerPlugin) {
...localJsFiles,
];
fs.writeFileSync(path.join(destDir, 'index.html'), buildHtml({ jsFiles, cssFiles, srcDir: demo.srcDir }));
if (IS_GENERATE_MANIFESTS) writeDemoManifest(demo, destDir);
}

return { ok: true };
Expand All @@ -702,30 +716,19 @@ async function main() {
console.log(`Batch concurrency: ${BATCH_CONCURRENCY}`);
if (SHARD_TOTAL > 1) console.log(`Shard: ${SHARD_INDEX}/${SHARD_TOTAL}`);
console.log(`Source: ${SRC_DEMOS_DIR}`);
console.log(`Output: ${OUT_ROOT}`);
console.log(`Output: ${SRC_DEMOS_DIR}`);
if (FILTER) console.log(`Filter: ${FILTER}`);
console.log('');

// IN_PLACE writes into the demo's own source folder, which must not be wiped.
if (!IN_PLACE && fs.existsSync(OUT_ROOT)) {
const existingWidgets = fs.readdirSync(OUT_ROOT, { withFileTypes: true }).filter((w) => w.isDirectory());
for (const widget of existingWidgets) {
const widgetPath = path.join(OUT_ROOT, widget.name);
const existingDemos = fs.readdirSync(widgetPath, { withFileTypes: true }).filter((d) => d.isDirectory());
for (const demo of existingDemos) {
const fwDir = path.join(widgetPath, demo.name, FRAMEWORK);
if (fs.existsSync(fwDir)) fs.rmSync(fwDir, { recursive: true, force: true });
}
}
}
fs.mkdirSync(OUT_ROOT, { recursive: true });
fs.mkdirSync(SRC_DEMOS_DIR, { recursive: true });

if (fs.existsSync(GENERATED_TSCONFIG_DIR)) {
fs.rmSync(GENERATED_TSCONFIG_DIR, { recursive: true, force: true });
}
// Shared across every demo's own build (see makeBuildOptions' chunkNames) — not owned by
// any single demo's own folder, so not covered by the per-demo wipe above; always safe to
// fully regenerate since chunk filenames are content-hashed.
const chunksDir = path.join(OUT_ROOT, CHUNKS_DIRNAME);
const chunksDir = path.join(SRC_DEMOS_DIR, CHUNKS_DIRNAME);
if (fs.existsSync(chunksDir)) {
fs.rmSync(chunksDir, { recursive: true, force: true });
}
Expand Down
42 changes: 22 additions & 20 deletions apps/demos/utils/server/csp-bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ if (!SUPPORTED.includes(FRAMEWORK)) {

const IS_ANGULAR = FRAMEWORK === 'Angular';

const IN_PLACE = process.env.BUNDLE_IN_PLACE === '1';
const IS_GENERATE_MANIFESTS = process.env.CSP_BUNDLE_GENERATE_MANIFESTS === '1';

const DEMOS_APP_ROOT = path.join(__dirname, '..', '..');
const SRC_DEMOS_DIR = path.join(DEMOS_APP_ROOT, 'Demos');
const OUT_ROOT = IN_PLACE ? SRC_DEMOS_DIR : path.join(DEMOS_APP_ROOT, 'csp-bundled-demos');
const NODE_MODULES = path.join(DEMOS_APP_ROOT, 'node_modules');

const CONCURRENCY = (() => {
Expand Down Expand Up @@ -244,6 +243,21 @@ function buildHtml({
`;
}

function writeDemoManifest({
srcDir, destDir, framework,
}) {
// eslint-disable-next-line global-require
const { discoverDemoSpecifiers, resolvePackageVersions } = require('./vendor-bundle');
const packages = resolvePackageVersions([
...discoverDemoSpecifiers(srcDir),
'devextreme-dist',
]);
fs.writeFileSync(
path.join(destDir, 'demo.manifest.json'),
JSON.stringify({ framework, packages }, null, 2),
);
}

async function bundleDemoTo({ srcDir, destDir, framework }) {
const entry = findEntry(srcDir);
if (!entry) return { ok: false, reason: 'no entry point (index.tsx|ts|jsx|js)' };
Expand Down Expand Up @@ -277,11 +291,13 @@ async function bundleDemoTo({ srcDir, destDir, framework }) {
cssFiles.push('bundle.css');
}

if (IS_GENERATE_MANIFESTS) writeDemoManifest({ srcDir, destDir, framework });

return { ok: true, jsFiles, cssFiles };
}

async function bundleDemo(demo, { destDir: destDirOverride, framework = FRAMEWORK } = {}) {
const destDir = destDirOverride || path.join(OUT_ROOT, demo.widget, demo.name, FRAMEWORK);
const destDir = destDirOverride || path.join(SRC_DEMOS_DIR, demo.widget, demo.name, FRAMEWORK);
const result = await bundleDemoTo({ srcDir: demo.srcDir, destDir, framework });
if (!result.ok) return result;

Expand Down Expand Up @@ -311,23 +327,9 @@ async function runPool(items, concurrency, fn) {
async function main() {
console.log(`Framework: ${FRAMEWORK}`);
console.log(`Concurrency: ${CONCURRENCY}`);
console.log(`Source: ${SRC_DEMOS_DIR}`);
console.log(`Output: ${OUT_ROOT}\n`);

// IN_PLACE writes into the demo's own source folder, which must not be wiped.
if (!IN_PLACE && fs.existsSync(OUT_ROOT)) {
const existingWidgets = fs.readdirSync(OUT_ROOT, { withFileTypes: true })
.filter((w) => w.isDirectory());
for (const widget of existingWidgets) {
const existingDemos = fs.readdirSync(path.join(OUT_ROOT, widget.name), { withFileTypes: true })
.filter((d) => d.isDirectory());
for (const demo of existingDemos) {
const fwDir = path.join(OUT_ROOT, widget.name, demo.name, FRAMEWORK);
if (fs.existsSync(fwDir)) fs.rmSync(fwDir, { recursive: true, force: true });
}
}
}
fs.mkdirSync(OUT_ROOT, { recursive: true });
console.log(`Output: ${SRC_DEMOS_DIR}\n`);

fs.mkdirSync(SRC_DEMOS_DIR, { recursive: true });

const allDemos = findDemos();
const demos = applyShard(allDemos);
Expand Down
Loading
Loading