diff --git a/.github/renovate.json b/.github/renovate.json index df3d3d21ff4a..7d602be924f7 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -85,8 +85,7 @@ "devexpress-gantt", "devexpress-diagram", "rrule", - "sass-embedded", - "systemjs" + "sass-embedded" ], "enabled": false }, diff --git a/.github/workflows/publish-demos.yml b/.github/workflows/publish-demos.yml index 484f55024eee..a8ad68d8d561 100644 --- a/.github/workflows/publish-demos.yml +++ b/.github/workflows/publish-demos.yml @@ -150,10 +150,6 @@ jobs: working-directory: apps/demos run: pnpm add ../../devextreme-installer.tgz ../../devextreme-dist-installer.tgz ../../devextreme-react-installer.tgz ../../devextreme-vue-installer.tgz ../../devextreme-angular-installer.tgz - - name: Prepare configs - working-directory: apps/demos - run: pnpm run prepare-js - - name: Copy metadata working-directory: apps/demos run: pnpm run make-demos-bundle --copy-metadata @@ -249,10 +245,6 @@ jobs: working-directory: apps/demos run: pnpm add ../../devextreme-installer.tgz ../../devextreme-dist-installer.tgz ../../devextreme-react-installer.tgz ../../devextreme-vue-installer.tgz ../../devextreme-angular-installer.tgz - - name: Prepare configs - working-directory: apps/demos - run: pnpm run prepare-js - - name: Copy metadata working-directory: apps/demos run: pnpm run make-demos-bundle --copy-metadata diff --git a/.github/workflows/visual-tests-demos.yml b/.github/workflows/visual-tests-demos.yml index 54c485c1817d..fe35f0d98595 100644 --- a/.github/workflows/visual-tests-demos.yml +++ b/.github/workflows/visual-tests-demos.yml @@ -272,20 +272,90 @@ 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 + timeout-minutes: 15 + needs: [check-should-run, determine-framework-tests-scope, build-devextreme] + if: | + always() && + needs.check-should-run.outputs.should-run == 'true' && + needs.determine-framework-tests-scope.result == 'success' && + needs.determine-framework-tests-scope.outputs.framework-tests-scope != 'none' && + needs.build-devextreme.result == 'success' + env: + NODE_OPTIONS: --max-old-space-size=8192 + steps: + - name: Get sources + uses: actions/checkout@v6 + + - name: Use Node.js + uses: actions/setup-node@v6 + with: + node-version-file: '.node-version' + + - name: Download devextreme sources + uses: actions/download-artifact@v8 + with: + name: devextreme-sources + + - uses: pnpm/action-setup@v6 + with: + run_install: false + + - name: Get pnpm store directory + shell: bash + run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache/restore@v5 + name: Restore pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-cache-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-cache + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Install tgz + working-directory: apps/demos + run: pnpm add --ignore-workspace --allow-build=core-js --allow-build=inferno devextreme-aspnet-data@5.1.0 devextreme-aspnet-data-nojquery@5.1.0 ../../devextreme-installer.tgz ../../devextreme-dist-installer.tgz ../../devextreme-react-installer.tgz ../../devextreme-vue-installer.tgz ../../devextreme-angular-installer.tgz && rm -f pnpm-workspace.yaml pnpm-lock.yaml + + - name: Build vendor bundles + working-directory: apps/demos + run: node utils/server/build-vendor-bundles.js + + - name: Copy vendor bundles + uses: actions/upload-artifact@v7 + with: + name: devextreme-vendor-bundles + path: apps/demos/bundles/vendor + retention-days: 1 build-demos: + name: ${{ matrix.SHARD_TOTAL == 1 && format('Build demos ({0})', matrix.FRAMEWORK) || format('Build demos ({0} {1}/{2})', matrix.FRAMEWORK, matrix.SHARD_INDEX, matrix.SHARD_TOTAL) }} runs-on: devextreme-shr2 - name: Build Demos Bundles timeout-minutes: 30 - needs: [check-should-run, determine-framework-tests-scope, build-devextreme] + needs: [check-should-run, determine-framework-tests-scope, build-devextreme, build-vendor-bundles] if: | always() && needs.check-should-run.outputs.should-run == 'true' && needs.determine-framework-tests-scope.result == 'success' && needs.determine-framework-tests-scope.outputs.framework-tests-scope != 'none' && - needs.build-devextreme.result == 'success' + needs.build-devextreme.result == 'success' && + needs.build-vendor-bundles.result == 'success' env: NODE_OPTIONS: --max-old-space-size=8192 + strategy: + fail-fast: false + matrix: + include: + - { FRAMEWORK: React, SHARD_INDEX: 1, SHARD_TOTAL: 1 } + - { FRAMEWORK: Vue, SHARD_INDEX: 1, SHARD_TOTAL: 1 } + - { FRAMEWORK: Angular, SHARD_INDEX: 1, SHARD_TOTAL: 3 } + - { FRAMEWORK: Angular, SHARD_INDEX: 2, SHARD_TOTAL: 3 } + - { FRAMEWORK: Angular, SHARD_INDEX: 3, SHARD_TOTAL: 3 } steps: - name: Get sources @@ -331,20 +401,39 @@ jobs: # - name: Link wrappers packages # run: pnpm install --frozen-lockfile - - name: Prepare bundles + # No-op for Angular (no manifest — see build-vendor-bundles). + - name: Download vendor bundles + uses: actions/download-artifact@v8 + with: + name: devextreme-vendor-bundles + path: apps/demos/bundles/vendor + + - name: Build demo bundles working-directory: apps/demos - run: pnpm exec nx prepare-bundles + env: + BUNDLE_IN_PLACE: '1' + CSP_SHARD_INDEX: ${{ matrix.SHARD_INDEX }} + CSP_SHARD_TOTAL: ${{ matrix.SHARD_TOTAL }} + run: node utils/server/csp-bundle.js --framework=${{ matrix.FRAMEWORK }} - name: Demos - Run tsc + if: matrix.FRAMEWORK == 'React' && matrix.SHARD_INDEX == 1 # global check, only needs to run once working-directory: apps/demos run: pnpm exec tsc --noEmit + # Packed into one tarball — a glob path would lose the Demos/ prefix on extract. + - name: Pack demo bundles + working-directory: apps/demos + run: | + find Demos \( -name 'bundle.js' -o -name 'bundle.css' \) -print0 \ + | tar -czf demo-bundles.tgz --null --files-from - + tar -tzf demo-bundles.tgz | head -3 + - name: Copy build artifacts uses: actions/upload-artifact@v7 with: - name: devextreme-bundles - path: | - apps/demos/bundles/ + name: devextreme-bundles-${{ matrix.FRAMEWORK }}${{ matrix.SHARD_TOTAL != 1 && format('-shard{0}', matrix.SHARD_INDEX) || '' }} + path: apps/demos/demo-bundles.tgz retention-days: 1 lint: @@ -579,11 +668,6 @@ jobs: working-directory: apps/demos run: pnpm add --ignore-workspace --allow-build=core-js --allow-build=inferno devextreme-aspnet-data@5.1.0 devextreme-aspnet-data-nojquery@5.1.0 ../../devextreme-installer.tgz ../../devextreme-dist-installer.tgz ../../devextreme-react-installer.tgz ../../devextreme-vue-installer.tgz ../../devextreme-angular-installer.tgz && rm -f pnpm-workspace.yaml pnpm-lock.yaml - - name: Prepare JS - if: steps.changed-react-demos.outputs.has-react-demos == 'true' - working-directory: apps/demos - run: pnpm run prepare-js - - name: Check generated JS demos if: steps.changed-react-demos.outputs.has-react-demos == 'true' working-directory: apps/demos @@ -651,10 +735,6 @@ jobs: working-directory: apps/demos run: pnpm add --ignore-workspace --allow-build=core-js --allow-build=inferno devextreme-aspnet-data@5.1.0 devextreme-aspnet-data-nojquery@5.1.0 ../../devextreme-installer.tgz ../../devextreme-dist-installer.tgz ../../devextreme-react-installer.tgz ../../devextreme-vue-installer.tgz ../../devextreme-angular-installer.tgz && rm -f pnpm-workspace.yaml pnpm-lock.yaml - - name: Prepare JS - working-directory: apps/demos - run: pnpm run prepare-js - - name: Check generated JS demos working-directory: apps/demos run: | @@ -830,7 +910,7 @@ jobs: if-no-files-found: ignore testcafe-frameworks-all: - needs: [check-should-run, determine-framework-tests-scope, build-demos] + needs: [check-should-run, determine-framework-tests-scope, build-demos, build-vendor-bundles] if: | always() && needs.check-should-run.outputs.should-run == 'true' && @@ -909,22 +989,62 @@ jobs: working-directory: apps/demos run: pnpm add --ignore-workspace --allow-build=core-js --allow-build=inferno devextreme-aspnet-data@5.1.0 devextreme-aspnet-data-nojquery@5.1.0 ../../devextreme-installer.tgz ../../devextreme-dist-installer.tgz ../../devextreme-react-installer.tgz ../../devextreme-vue-installer.tgz ../../devextreme-angular-installer.tgz && rm -f pnpm-workspace.yaml pnpm-lock.yaml - - name: Prepare JS - working-directory: apps/demos - run: pnpm run prepare-js + - name: Download demo bundles (React) + if: startsWith(matrix.CONSTEL, 'react') + uses: actions/download-artifact@v8 + with: + name: devextreme-bundles-React + path: apps/demos/.demo-bundles/react - - name: Update bundles config - working-directory: apps/demos - run: pnpm run update-config + - name: Download demo bundles (Vue) + if: startsWith(matrix.CONSTEL, 'vue') + uses: actions/download-artifact@v8 + with: + name: devextreme-bundles-Vue + path: apps/demos/.demo-bundles/vue + + # Angular is built in 3 shards but tested in 10 — every test shard needs all 3. + - name: Download demo bundles (Angular shard 1) + if: startsWith(matrix.CONSTEL, 'angular') + uses: actions/download-artifact@v8 + with: + name: devextreme-bundles-Angular-shard1 + path: apps/demos/.demo-bundles/angular-shard1 + + - name: Download demo bundles (Angular shard 2) + if: startsWith(matrix.CONSTEL, 'angular') + uses: actions/download-artifact@v8 + with: + name: devextreme-bundles-Angular-shard2 + path: apps/demos/.demo-bundles/angular-shard2 - - name: Create bundles dir - run: mkdir -p apps/demos/bundles + - name: Download demo bundles (Angular shard 3) + if: startsWith(matrix.CONSTEL, 'angular') + uses: actions/download-artifact@v8 + with: + name: devextreme-bundles-Angular-shard3 + path: apps/demos/.demo-bundles/angular-shard3 - - name: Download bundles artifacts + # No-op for angular demos (no manifest — see build-vendor-bundles.js). + - name: Download vendor bundles uses: actions/download-artifact@v8 with: - name: devextreme-bundles - path: apps/demos/bundles + name: devextreme-vendor-bundles + path: apps/demos/bundles/vendor + + - name: Unpack demo bundles + run: | + shopt -s nullglob + archives=(apps/demos/.demo-bundles/*/demo-bundles.tgz) + if [ ${#archives[@]} -eq 0 ]; then + echo "No demo bundle archives downloaded for constellation '${{ matrix.CONSTEL }}'" >&2 + exit 1 + fi + for archive in "${archives[@]}"; do + tar -xzf "$archive" -C apps/demos + done + rm -rf apps/demos/.demo-bundles + echo "Unpacked $(find apps/demos/Demos -name 'bundle.js' | wc -l) demo bundle(s)" - name: Run Web Server run: | @@ -978,7 +1098,7 @@ jobs: if-no-files-found: ignore testcafe-frameworks-changed: - needs: [check-should-run, determine-framework-tests-scope, build-demos] + needs: [check-should-run, determine-framework-tests-scope, build-demos, build-vendor-bundles] if: | always() && needs.check-should-run.outputs.should-run == 'true' && @@ -1045,22 +1165,41 @@ jobs: working-directory: apps/demos run: pnpm add --ignore-workspace --allow-build=core-js --allow-build=inferno devextreme-aspnet-data@5.1.0 devextreme-aspnet-data-nojquery@5.1.0 ../../devextreme-installer.tgz ../../devextreme-dist-installer.tgz ../../devextreme-react-installer.tgz ../../devextreme-vue-installer.tgz ../../devextreme-angular-installer.tgz && rm -f pnpm-workspace.yaml pnpm-lock.yaml - - name: Prepare JS - working-directory: apps/demos - run: pnpm run prepare-js + - name: Download demo bundles (React) + if: matrix.CONSTEL == 'react' + uses: actions/download-artifact@v8 + with: + name: devextreme-bundles-React + path: apps/demos/.demo-bundles/react - - name: Update bundles config - working-directory: apps/demos - run: pnpm run update-config + - name: Download demo bundles (Vue) + if: matrix.CONSTEL == 'vue' + uses: actions/download-artifact@v8 + with: + name: devextreme-bundles-Vue + path: apps/demos/.demo-bundles/vue - - name: Create bundles dir - run: mkdir -p apps/demos/bundles + # build-demos shards Angular 3 ways regardless of how it's tested here. + - name: Download demo bundles (Angular shard 1) + if: matrix.CONSTEL == 'angular' + uses: actions/download-artifact@v8 + with: + name: devextreme-bundles-Angular-shard1 + path: apps/demos/.demo-bundles/angular-shard1 - - name: Download bundles artifacts + - name: Download demo bundles (Angular shard 2) + if: matrix.CONSTEL == 'angular' uses: actions/download-artifact@v8 with: - name: devextreme-bundles - path: apps/demos/bundles + name: devextreme-bundles-Angular-shard2 + path: apps/demos/.demo-bundles/angular-shard2 + + - name: Download demo bundles (Angular shard 3) + if: matrix.CONSTEL == 'angular' + uses: actions/download-artifact@v8 + with: + name: devextreme-bundles-Angular-shard3 + path: apps/demos/.demo-bundles/angular-shard3 - name: Download changes artifacts uses: actions/download-artifact@v8 @@ -1068,6 +1207,27 @@ jobs: name: changed-demos path: apps/demos + # No-op for angular demos (no manifest — see build-vendor-bundles.js). + - name: Download vendor bundles + uses: actions/download-artifact@v8 + with: + name: devextreme-vendor-bundles + path: apps/demos/bundles/vendor + + - name: Unpack demo bundles + run: | + shopt -s nullglob + archives=(apps/demos/.demo-bundles/*/demo-bundles.tgz) + if [ ${#archives[@]} -eq 0 ]; then + echo "No demo bundle archives downloaded for constellation '${{ matrix.CONSTEL }}'" >&2 + exit 1 + fi + for archive in "${archives[@]}"; do + tar -xzf "$archive" -C apps/demos + done + rm -rf apps/demos/.demo-bundles + echo "Unpacked $(find apps/demos/Demos -name 'bundle.js' | wc -l) demo bundle(s)" + - name: Run Web Server run: | python -m http.server 8080 & @@ -1163,8 +1323,6 @@ jobs: needs.build-devextreme.result == 'success' runs-on: devextreme-shr2 timeout-minutes: 60 - env: - CSP_USE_BUNDLED: '0' steps: - name: Get sources @@ -1251,8 +1409,6 @@ jobs: - { FRAMEWORK: Angular, SHARD_INDEX: 3, SHARD_TOTAL: 3 } runs-on: devextreme-shr2 timeout-minutes: 60 - env: - CSP_USE_BUNDLED: '1' steps: - name: Get sources @@ -1296,12 +1452,11 @@ jobs: working-directory: apps/demos run: pnpm add --ignore-workspace --allow-build=core-js --allow-build=inferno devextreme-aspnet-data@5.1.0 devextreme-aspnet-data-nojquery@5.1.0 ../../devextreme-installer.tgz ../../devextreme-dist-installer.tgz ../../devextreme-react-installer.tgz ../../devextreme-vue-installer.tgz ../../devextreme-angular-installer.tgz && rm -f pnpm-workspace.yaml pnpm-lock.yaml - # Bundle production-style pages only when the CSP check is configured to - # read csp-bundled-demos; otherwise the checker uses SystemJS dev demos. + # Rebuilt independently rather than depending on build-demos, to keep timing decoupled. - name: Bundle demos for CSP check - if: env.CSP_USE_BUNDLED == '1' || env.CSP_USE_BUNDLED == 'true' working-directory: apps/demos env: + BUNDLE_IN_PLACE: '1' CSP_SHARD_INDEX: ${{ matrix.SHARD_INDEX }} CSP_SHARD_TOTAL: ${{ matrix.SHARD_TOTAL }} run: node utils/server/csp-bundle.js --framework=${{ matrix.FRAMEWORK }} diff --git a/CLAUDE.md b/CLAUDE.md index e74441cff8e4..0398b347cf3c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -114,7 +114,7 @@ packages/ testcafe-models/ # TestCafe page object models apps/ - demos/ # Technical demos (Angular, React, Vue, jQuery) + demos/ # Technical demos (Angular, React, Vue, jQuery) — esbuild on demand e2e/ testcafe-devextreme/ # E2E tests @@ -142,6 +142,31 @@ Do not edit directly: - `packages/devextreme/js/__internal/core/localization/default_messages.ts` - `packages/devextreme/js/__internal/core/localization/cldr-data/**/*` +## Demos + +Technical demos live in `apps/demos`. + +From the repo root: + +```bash +pnpm run demos:prepare # build DevExtreme, pack wrappers, vendor bundles +pnpm run demos:start # http://localhost:8080/ +``` + +`demos:start` is long-running. Do not start a second copy if the developer already has it running. + +Angular, React, and Vue demos bundle on demand with esbuild when a page is opened (`bundle.js` / `bundle.css` next to the demo source — gitignored, do not commit). jQuery demos load `dx.all.js` from `devextreme-dist`. + +### Editing demos + +From `apps/demos`: + +```bash +pnpm run add-demo # scaffold a new demo +pnpm run convert-to-js split # after React TypeScript changes +pnpm run fix-lint +``` + ## Code Style Conventions These rules apply to `packages/devextreme/js/**/*.d.ts` (see `.github/instructions/API_conventions.instructions.md`): diff --git a/apps/demos/.gitignore b/apps/demos/.gitignore index f6c9d4263dc9..66c29c017ee4 100644 --- a/apps/demos/.gitignore +++ b/apps/demos/.gitignore @@ -23,11 +23,13 @@ changed-files.json !.vscode/settings.json -shared/empty-file.js - -Demos/**/config.js Demos/**/tsconfig.json +Demos/**/bundle.js +Demos/**/bundle.css +Demos/**/demo.manifest.json +Demos/_chunks + .DS_Store **/.DS_Store publish-demos @@ -39,5 +41,8 @@ csp-bundled-demos utils/server/.csp-bundle-angular-* Demos/**/.csp-bundle-angular-patched.*.ts +# Scratch entry files from utils/server/vendor-bundle.js (cleaned up on success). +.vendor-entry-tmp + .angular angular.json diff --git a/apps/demos/.prettierrc.json b/apps/demos/.prettierrc.json index b4c6d26502b7..4f5977701e68 100644 --- a/apps/demos/.prettierrc.json +++ b/apps/demos/.prettierrc.json @@ -16,13 +16,6 @@ "singleAttributePerLine": true, "trailingComma": "all" } - }, - { - "files": "**/ReactJs/**/*.html", - "options": { - "printWidth": 100, - "singleAttributePerLine": true - } } ] } diff --git a/apps/demos/Demos/Accordion/Overview/Angular/app/app.component.ts b/apps/demos/Demos/Accordion/Overview/Angular/app/app.component.ts index 6e3fec7d805d..17f2a91838cc 100644 --- a/apps/demos/Demos/Accordion/Overview/Angular/app/app.component.ts +++ b/apps/demos/Demos/Accordion/Overview/Angular/app/app.component.ts @@ -15,17 +15,11 @@ if (!/localhost/.test(document.location.host)) { enableProdMode(); } -let modulePrefix = ''; -// @ts-ignore -if (window && window.config?.packageConfigPaths) { - modulePrefix = '/app'; -} - @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'demo-app', - templateUrl: `.${modulePrefix}/app.component.html`, - styleUrls: [`.${modulePrefix}/app.component.css`], + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'], providers: [Service], preserveWhitespaces: true, imports: [ diff --git a/apps/demos/Demos/Accordion/Overview/Angular/index.html b/apps/demos/Demos/Accordion/Overview/Angular/index.html index 1ab1fb54a1df..7dd155b53981 100644 --- a/apps/demos/Demos/Accordion/Overview/Angular/index.html +++ b/apps/demos/Demos/Accordion/Overview/Angular/index.html @@ -1,26 +1,17 @@ - +