diff --git a/.github/workflows/applications.yml b/.github/workflows/applications.yml index a80546e63..aab90cad8 100644 --- a/.github/workflows/applications.yml +++ b/.github/workflows/applications.yml @@ -5,9 +5,9 @@ on: pull_request: workflow_call: inputs: - external-tools-version: - description: 'Version of external tools' - default: latest + deps-version-tag: + description: 'Version tag of scaffolder and framework' + default: '' required: false type: string @@ -21,16 +21,19 @@ jobs: - vue-v3 - react - react-ts + - react-swc + - react-swc-ts + - nextjs + - nextjs-ts NODE: - 18 OS: - ubuntu-latest - - windows-latest runs-on: ${{ matrix.OS }} env: VUE_CLI_CONFIG_PATH: $GITHUB_WORKSPACE/.vuerc - TOOLING_VERSION_PARAM: ${{ inputs.external-tools-version != '' && format('--tooling-version {0}', inputs.external-tools-version) || '' }} + DEPS_VERSION_TAG_PARAM: ${{ inputs.deps-version-tag != '' && format('--deps-version-tag {0}', inputs.deps-version-tag) || '' }} name: ${{ matrix.APPROACH }}, node ${{ matrix.NODE }}, ${{ matrix.OS }} @@ -52,7 +55,7 @@ jobs: run: npm config set legacy-peer-deps true - name: Create ${{ matrix.APPROACH }} application - run: npm run create-template -- -- -e ${{ matrix.APPROACH }} ${{ env.TOOLING_VERSION_PARAM }} + run: npm run create-template -- -- -e ${{ matrix.APPROACH }} ${{ env.DEPS_VERSION_TAG_PARAM }} timeout-minutes: 30 - name: Install Internal Packages @@ -62,14 +65,14 @@ jobs: - name: Lint created application if: ${{ matrix.OS != 'windows-latest' }} - run: npm run lint-template -- -- -e ${{ matrix.APPROACH }} ${{ env.TOOLING_VERSION_PARAM }} + run: npm run lint-template -- -- -e ${{ matrix.APPROACH }} ${{ env.DEPS_VERSION_TAG_PARAM }} - name: Run template tests if: ${{ matrix.OS != 'windows-latest' }} env: LAUNCH_BROWSER: true CHROME_PATH: ${{ steps.setup-chrome.outputs.chrome-path }} - run: npm run test-template -- -- -e ${{ matrix.APPROACH }} ${{ env.TOOLING_VERSION_PARAM }} + run: npm run test-template -- -- -e ${{ matrix.APPROACH }} ${{ env.DEPS_VERSION_TAG_PARAM }} timeout-minutes: 40 - name: Archive artifacts diff --git a/.github/workflows/check-nextjs.yml b/.github/workflows/check-nextjs.yml new file mode 100644 index 000000000..f3c5071ee --- /dev/null +++ b/.github/workflows/check-nextjs.yml @@ -0,0 +1,94 @@ +name: Check "add devextreme-react" for NextJS app + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + nextjs-devextreme-test: + strategy: + fail-fast: false + matrix: + TYPESCRIPT: [true, false] + SRC_DIR: [true, false] + APP_ROUTER: [true, false] + NODE: + - 18 + OS: + - ubuntu-latest + + runs-on: ${{ matrix.OS }} + name: Next.js + DevExtreme (TS:${{ matrix.TYPESCRIPT }}, src:${{ matrix.SRC_DIR }}, app-router:${{ matrix.APP_ROUTER }}), node ${{ matrix.NODE }}, ${{ matrix.OS }} + + steps: + - name: Get sources + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.NODE }} + cache: 'npm' + + - name: Extract create-next-app version + run: | + NEXT_APP_VERSION=$(node -e "const versions = require('./packages/devextreme-cli/src/utility/latest-versions.js'); console.log(versions['create-next-app'])") + echo "Using create-next-app version: $NEXT_APP_VERSION" + echo "NEXT_APP_VERSION=$NEXT_APP_VERSION" >> $GITHUB_ENV + shell: bash + + - name: Create Next.js application + run: | + npx create-next-app@${{ env.NEXT_APP_VERSION }} test-nextjs-app \ + --typescript=${{ matrix.TYPESCRIPT }} \ + --src-dir=${{ matrix.SRC_DIR }} \ + --app=${{ matrix.APP_ROUTER }} \ + --eslint \ + --no-tailwind \ + --import-alias="@/*" \ + --no-git \ + --use-npm + shell: bash + + - name: Add actual devExtreme-cli + run: | + cd test-nextjs-app + npm add devextreme-cli + rm -r ./node_modules/devextreme-cli/src/ + cp -r ../packages/devextreme-cli/src/ ./node_modules/devextreme-cli/ + ls ./node_modules/devextreme-cli + ls ./node_modules/devextreme-cli/src + shell: bash + timeout-minutes: 15 + + - name: Add DevExtreme to Next.js application + run: | + cd test-nextjs-app + npx devextreme-cli add devextreme-react + shell: bash + timeout-minutes: 15 + + - name: Verify DevExtreme dependencies in package.json + run: | + cd test-nextjs-app + + if ! grep -q '"devextreme":' package.json; then + echo "Error: devextreme dependency not found in package.json" + exit 1 + fi + + if ! grep -q '"devextreme-react":' package.json; then + echo "Error: devextreme-react dependency not found in package.json" + exit 1 + fi + + echo "DevExtreme dependencies successfully installed" + shell: bash + + - name: Build Next.js application + run: | + cd test-nextjs-app + npm run build + shell: bash + timeout-minutes: 15 diff --git a/.github/workflows/schedule-cli-tests-latest-version.yml b/.github/workflows/schedule-cli-tests-latest-version.yml index 45e3ce573..7fad5e04e 100644 --- a/.github/workflows/schedule-cli-tests-latest-version.yml +++ b/.github/workflows/schedule-cli-tests-latest-version.yml @@ -10,7 +10,7 @@ jobs: name: Run CLI tests uses: ./.github/workflows/applications.yml with: - external-tools-version: latest + deps-version-tag: latest teams_notification_on_failure: name: Send Teams notification diff --git a/.github/workflows/schedule-cli-tests-next-version.yml b/.github/workflows/schedule-cli-tests-next-version.yml index 8b2cf28f8..e8ba0d7d0 100644 --- a/.github/workflows/schedule-cli-tests-next-version.yml +++ b/.github/workflows/schedule-cli-tests-next-version.yml @@ -2,15 +2,15 @@ name: Schedule tests for next versions of external CLIs on: workflow_dispatch: - schedule: - - cron: "0 1 * * 1" + # schedule: + # - cron: "0 1 * * 1" jobs: call_tests_workflow: name: Run CLI tests uses: ./.github/workflows/applications.yml with: - external-tools-version: next + deps-version-tag: next teams_notification_on_failure: name: Send Teams notification diff --git a/package-lock.json b/package-lock.json index 172d932c7..02118673f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,62 +15,6 @@ "puppeteer": "^21.11.0" } }, - "node_modules/@angular-devkit/core": { - "version": "16.1.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.1.2.tgz", - "integrity": "sha512-RGSkcu03Zybg7drenKxBHvQ/xF8LLZR7o09S4vtLVDwMTGW/ZHnZ+YSC0OuRddbtBlBRhjmsQN6HPvXXfwGLkQ==", - "dev": true, - "peer": true, - "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.0", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^16.14.0 || >=18.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/core/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@angular-devkit/schematics": { - "version": "16.1.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.1.2.tgz", - "integrity": "sha512-5CRHrurFwJh7IPpo56DbIjcqzI1usraYwjHKZFeXoW4sQTRP5yeSuJoBM9zuBX5ZFQVCRt24j5DQTlsGk7Ky8Q==", - "dev": true, - "peer": true, - "dependencies": { - "@angular-devkit/core": "16.1.2", - "jsonc-parser": "3.2.0", - "magic-string": "0.30.0", - "ora": "5.4.1", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^16.14.0 || >=18.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, "node_modules/@angular-eslint/schematics": { "version": "0.2.0-beta.1", "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-0.2.0-beta.1.tgz", @@ -201,13 +145,6 @@ "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", "dev": true }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true, - "peer": true - }, "node_modules/@lerna/add": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/@lerna/add/-/add-5.6.2.tgz", @@ -1955,41 +1892,6 @@ "node": ">=8" } }, - "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "peer": true, - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -3348,13 +3250,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "peer": true - }, "node_modules/fast-fifo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", @@ -4539,13 +4434,6 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "peer": true - }, "node_modules/json-stringify-nice": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz", @@ -4853,19 +4741,6 @@ "node": ">=12" } }, - "node_modules/magic-string": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", - "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", - "dev": true, - "peer": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.13" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -6708,16 +6583,6 @@ "once": "^1.3.1" } }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/puppeteer": { "version": "21.11.0", "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-21.11.0.tgz", @@ -6779,21 +6644,6 @@ } } }, - "node_modules/puppeteer/node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true, - "optional": true, - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -7163,16 +7013,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -8034,16 +7874,6 @@ "yarn": "*" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "peer": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/urlpattern-polyfill": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", @@ -8427,43 +8257,6 @@ } }, "dependencies": { - "@angular-devkit/core": { - "version": "16.1.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.1.2.tgz", - "integrity": "sha512-RGSkcu03Zybg7drenKxBHvQ/xF8LLZR7o09S4vtLVDwMTGW/ZHnZ+YSC0OuRddbtBlBRhjmsQN6HPvXXfwGLkQ==", - "dev": true, - "peer": true, - "requires": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.0", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "dependencies": { - "source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "peer": true - } - } - }, - "@angular-devkit/schematics": { - "version": "16.1.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.1.2.tgz", - "integrity": "sha512-5CRHrurFwJh7IPpo56DbIjcqzI1usraYwjHKZFeXoW4sQTRP5yeSuJoBM9zuBX5ZFQVCRt24j5DQTlsGk7Ky8Q==", - "dev": true, - "peer": true, - "requires": { - "@angular-devkit/core": "16.1.2", - "jsonc-parser": "3.2.0", - "magic-string": "0.30.0", - "ora": "5.4.1", - "rxjs": "7.8.1" - } - }, "@angular-eslint/schematics": { "version": "0.2.0-beta.1", "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-0.2.0-beta.1.tgz", @@ -8569,13 +8362,6 @@ "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", "dev": true }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true, - "peer": true - }, "@lerna/add": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/@lerna/add/-/add-5.6.2.tgz", @@ -9725,8 +9511,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "dev": true, - "requires": {} + "dev": true }, "@octokit/plugin-rest-endpoint-methods": { "version": "6.7.0", @@ -10002,29 +9787,6 @@ "indent-string": "^4.0.0" } }, - "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "peer": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "peer": true, - "requires": { - "ajv": "^8.0.0" - } - }, "ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -11056,13 +10818,6 @@ } } }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "peer": true - }, "fast-fifo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", @@ -11978,13 +11733,6 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "peer": true - }, "json-stringify-nice": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz", @@ -12233,16 +11981,6 @@ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true }, - "magic-string": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", - "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", - "dev": true, - "peer": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.4.13" - } - }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -13661,13 +13399,6 @@ "once": "^1.3.1" } }, - "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true, - "peer": true - }, "puppeteer": { "version": "21.11.0", "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-21.11.0.tgz", @@ -13690,14 +13421,6 @@ "js-yaml": "^4.1.0", "parse-json": "^5.2.0" } - }, - "typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", - "dev": true, - "optional": true, - "peer": true } } }, @@ -13994,13 +13717,6 @@ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "peer": true - }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -14660,16 +14376,6 @@ "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", "dev": true }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "peer": true, - "requires": { - "punycode": "^2.1.0" - } - }, "urlpattern-polyfill": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", @@ -14914,8 +14620,7 @@ "version": "8.16.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", - "dev": true, - "requires": {} + "dev": true }, "xtend": { "version": "4.0.2", diff --git a/packages/devextreme-cli/package-lock.json b/packages/devextreme-cli/package-lock.json index 7f35170dd..f4043a38f 100644 --- a/packages/devextreme-cli/package-lock.json +++ b/packages/devextreme-cli/package-lock.json @@ -26,6 +26,7 @@ "@typescript-eslint/eslint-plugin": "^4.33.0", "@typescript-eslint/parser": "^4.33.0", "babel-eslint": "^10.1.0", + "create-vite": "6.3.1", "cross-env": "^5.2.1", "eslint": "^7.32.0", "eslint-config-angular": "^0.5.0", @@ -45,7 +46,8 @@ "tree-kill": "^1.2.2", "tree-kill-promise": "^1.0.12", "typescript": "^4.0.2", - "typescript-eslint-parser": "^22.0.0" + "typescript-eslint-parser": "^22.0.0", + "wait-on": "8.0.0" }, "engines": { "node": ">12.6.0", @@ -641,6 +643,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", @@ -1702,6 +1721,30 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -2264,6 +2307,13 @@ "node": ">=8" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -2280,6 +2330,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/babel-eslint": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", @@ -2763,6 +2825,19 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2877,6 +2952,22 @@ "node": ">=8" } }, + "node_modules/create-vite": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/create-vite/-/create-vite-6.3.1.tgz", + "integrity": "sha512-kI3S6OgiBYnRlmhr4gNY76Fj62fMh+KukEd2PSAvMPdnY95rHhJCjCoUlt4BuHVNjfmRMbsFBBtO5dryFoSyUw==", + "dev": true, + "bin": { + "create-vite": "index.js", + "cva": "index.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + } + }, "node_modules/cross-env": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz", @@ -3044,6 +3135,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -3295,15 +3396,16 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -4338,6 +4440,27 @@ "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -4348,6 +4471,22 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -7056,6 +7195,20 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -7352,6 +7505,29 @@ "node": ">=4" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -7922,6 +8098,13 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -8165,6 +8348,23 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -9148,6 +9348,26 @@ "semver": "bin/semver.js" } }, + "node_modules/wait-on": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.0.tgz", + "integrity": "sha512-fNE5SXinLr2Bt7cJvjvLg2PcXfqznlqRvtE3f8AqYdRZ9BhE+XpsCp1mwQbRoO7s1q7uhAuCw0Ro3mG/KdZjEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "axios": "^1.7.4", + "joi": "^17.13.3", + "lodash": "^4.17.21", + "minimist": "^1.2.8", + "rxjs": "^7.8.1" + }, + "bin": { + "wait-on": "bin/wait-on" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -9831,6 +10051,21 @@ } } }, + "@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true + }, + "@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, "@humanwhocodes/config-array": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", @@ -10518,6 +10753,27 @@ "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", "optional": true }, + "@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "dev": true + }, + "@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true + }, "@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -10769,8 +11025,7 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} + "dev": true }, "ajv": { "version": "6.12.6", @@ -10923,6 +11178,12 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, "available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -10932,6 +11193,17 @@ "possible-typed-array-names": "^1.0.0" } }, + "axios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "dev": true, + "requires": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "babel-eslint": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", @@ -11273,6 +11545,15 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -11359,6 +11640,12 @@ } } }, + "create-vite": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/create-vite/-/create-vite-6.3.1.tgz", + "integrity": "sha512-kI3S6OgiBYnRlmhr4gNY76Fj62fMh+KukEd2PSAvMPdnY95rHhJCjCoUlt4BuHVNjfmRMbsFBBtO5dryFoSyUw==", + "dev": true + }, "cross-env": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz", @@ -11427,8 +11714,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "dev": true, - "requires": {} + "dev": true }, "deep-is": { "version": "0.1.4", @@ -11464,6 +11750,12 @@ "object-keys": "^1.1.1" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true + }, "detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -11657,14 +11949,15 @@ } }, "es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, "requires": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" } }, "es-shim-unscopables": { @@ -11911,8 +12204,7 @@ "version": "8.10.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", - "dev": true, - "requires": {} + "dev": true }, "eslint-plugin-angular": { "version": "4.1.0", @@ -12041,8 +12333,7 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", - "dev": true, - "requires": {} + "dev": true }, "eslint-plugin-unused-imports": { "version": "1.1.5", @@ -12406,6 +12697,12 @@ "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", "dev": true }, + "follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true + }, "for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -12415,6 +12712,18 @@ "is-callable": "^1.1.3" } }, + "form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -13753,8 +14062,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "requires": {} + "dev": true }, "jest-regex-util": { "version": "29.6.3", @@ -14321,6 +14629,19 @@ } } }, + "joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -14546,6 +14867,21 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "optional": true }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -14957,6 +15293,12 @@ } } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -15102,6 +15444,23 @@ "queue-microtask": "^1.2.2" } }, + "rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + } + } + }, "safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -15795,6 +16154,19 @@ } } }, + "wait-on": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.0.tgz", + "integrity": "sha512-fNE5SXinLr2Bt7cJvjvLg2PcXfqznlqRvtE3f8AqYdRZ9BhE+XpsCp1mwQbRoO7s1q7uhAuCw0Ro3mG/KdZjEw==", + "dev": true, + "requires": { + "axios": "^1.7.4", + "joi": "^17.13.3", + "lodash": "^4.17.21", + "minimist": "^1.2.8", + "rxjs": "^7.8.1" + } + }, "walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", diff --git a/packages/devextreme-cli/package.json b/packages/devextreme-cli/package.json index e0965ac5f..2433b20bf 100644 --- a/packages/devextreme-cli/package.json +++ b/packages/devextreme-cli/package.json @@ -52,6 +52,7 @@ "@typescript-eslint/parser": "^4.33.0", "babel-eslint": "^10.1.0", "cross-env": "^5.2.1", + "create-vite": "6.3.1", "eslint": "^7.32.0", "eslint-config-angular": "^0.5.0", "eslint-config-prettier": "^8.10.0", @@ -70,6 +71,7 @@ "tree-kill": "^1.2.2", "tree-kill-promise": "^1.0.12", "typescript": "^4.0.2", - "typescript-eslint-parser": "^22.0.0" + "typescript-eslint-parser": "^22.0.0", + "wait-on": "8.0.0" } } diff --git a/packages/devextreme-cli/src/application.js b/packages/devextreme-cli/src/application.js index 35a64a81d..a5a65228b 100644 --- a/packages/devextreme-cli/src/application.js +++ b/packages/devextreme-cli/src/application.js @@ -1,12 +1,34 @@ const angularApplication = require('./applications/application.angular'); const reactApplication = require('./applications/application.react'); +const nextjsApplication = require('./applications/application.nextjs'); const vueApplication = require('./applications/application.vue'); +const getReactAppType = require('./utility/prompts/react-app-type'); const printHelp = require('./help').printHelp; const isApplicationCommand = (command) => { return [ 'new', 'add' ].includes(command); }; +const handleWrongAppType = (appType, command) => { + console.error(`The '${appType}' application type is not valid`); + printHelp(command); +}; + +const createReact = async(appName, options, command) => { + const reactAppType = await getReactAppType(options['app-type']); + + switch(reactAppType) { + case 'vite': + await reactApplication.create(appName, options); + return; + case 'nextjs': + await nextjsApplication.create(appName, options); + return; + default: + handleWrongAppType(reactAppType, command); + } +}; + const run = async(commands, options, devextremeConfig) => { if(!commands[1]) { console.error('Command is incomplete. Please specify parameters.'); @@ -23,15 +45,15 @@ const run = async(commands, options, devextremeConfig) => { await angularApplication.create(appName, options); return; case 'react-app': - await reactApplication.create(appName, options); + await createReact(appName, options, commands[0]); return; case 'vue-app': await vueApplication.create(appName, options); return; default: - console.error(`The '${app}' application type is not valid`); - printHelp(commands[0]); + handleWrongAppType(app, commands[0]); } + } else { if(commands[0] === 'add') { if(commands[1] === 'devextreme-angular') { @@ -40,7 +62,12 @@ const run = async(commands, options, devextremeConfig) => { } if(commands[1] === 'devextreme-react') { - reactApplication.install(options); + if(nextjsApplication.isNextJsApp()) { + nextjsApplication.install(options); + } else { + reactApplication.install(options); + } + return; } @@ -54,23 +81,16 @@ const run = async(commands, options, devextremeConfig) => { return; } - if(devextremeConfig.applicationEngine === 'angular') { - if(commands[1] === 'view') { - angularApplication.addView(commands[2], options); - } else { - console.error('Invalid command'); - printHelp(commands[0]); - } - } else if(devextremeConfig.applicationEngine === 'react') { - if(commands[1] === 'view') { - reactApplication.addView(commands[2], options); - } else { - console.error('Invalid command'); - printHelp(commands[0]); - } - } else if(devextremeConfig.applicationEngine === 'vue') { + const app = { + 'angular': angularApplication, + 'react': reactApplication, + 'nextjs': nextjsApplication, + 'vue': vueApplication, + }[devextremeConfig.applicationEngine]; + + if(app) { if(commands[1] === 'view') { - vueApplication.addView(commands[2], options); + app.addView(commands[2], options); } else { console.error('Invalid command'); printHelp(commands[0]); diff --git a/packages/devextreme-cli/src/applications/application.angular.js b/packages/devextreme-cli/src/applications/application.angular.js index c164f8c97..222feab71 100644 --- a/packages/devextreme-cli/src/applications/application.angular.js +++ b/packages/devextreme-cli/src/applications/application.angular.js @@ -7,10 +7,13 @@ const fs = require('fs'); const dasherize = require('../utility/string').dasherize; const ngVersion = require('../utility/ng-version'); const latestVersions = require('../utility/latest-versions'); -const { extractToolingVersion, toolingVersionOptionName } = require('../utility/extract-tooling-version'); +const { extractDepsVersionTag, depsVersionTagOptionName } = require('../utility/extract-deps-version-tag'); +const { getPackageJsonPath } = require('../utility/package-json-utils'); +const modifyJson = require('../utility/modify-json-file'); const schematicsVersion = latestVersions['devextreme-schematics'] || 'latest'; const minNgCliVersion = new semver('17.0.0'); +const ngCliWithZoneless = new semver('20.0.0'); async function runSchematicCommand(schematicCommand, options, evaluatingOptions) { const collectionName = 'devextreme-schematics'; @@ -27,7 +30,7 @@ async function runSchematicCommand(schematicCommand, options, evaluatingOptions) const commandArguments = ['g', `${collectionName}:${schematicCommand}`]; - const { [toolingVersionOptionName]: _, ...optionsToArguments } = options; // eslint-disable-line no-unused-vars + const { [depsVersionTagOptionName]: _, ...optionsToArguments } = options; // eslint-disable-line no-unused-vars for(let option in optionsToArguments) { commandArguments.push(`--${dasherize(option)}=${options[option]}`); } @@ -37,12 +40,13 @@ async function runSchematicCommand(schematicCommand, options, evaluatingOptions) async function runNgCommand(commandArguments, commandOptions, commandConfig) { const hasNg = await hasSutableNgCli(); - const toolingVersion = extractToolingVersion(commandOptions); - const npmCommandName = hasNg && !toolingVersion ? 'ng' : 'npx'; + const depsVersionTag = extractDepsVersionTag(commandOptions); + const npmCommandName = hasNg && !depsVersionTag ? 'ng' : 'npx'; const [minCliLtsVersion] = minNgCliVersion.version.split('.'); - const ngCommandArguments = hasNg && !toolingVersion + + const ngCommandArguments = hasNg && !depsVersionTag ? [] - : ['-p', `@angular/cli@v${minCliLtsVersion}-lts`, 'ng']; + : ['-p', `@angular/cli@${depsVersionTag || `v${minCliLtsVersion}-lts`}`, 'ng']; ngCommandArguments.push(...commandArguments); return runCommand(npmCommandName, ngCommandArguments, commandConfig); @@ -60,6 +64,7 @@ function localPackageExists(packageName) { const hasSutableNgCli = async() => { const localVersion = ngVersion.getLocalNgVersion(); + if(!localVersion) { return false; } @@ -74,8 +79,28 @@ const install = async(options) => { }); }; +const bumpAngular = (appPath, versionTag) => { + modifyJson(getPackageJsonPath(appPath), ({ dependencies, devDependencies, ...rest }) => { + const bump = (section) => { + for(const depName in section) { + section[depName] = depName.startsWith('@angular') ? versionTag : section[depName]; + } + return section; + }; + + return { + dependencies: bump(dependencies), + devDependencies: bump(devDependencies), + ...rest, + }; + }); + +}; + const create = async(appName, options) => { const layout = await getLayoutInfo(options.layout); + const depsVersionTag = extractDepsVersionTag(options); + const currentNgVersion = ngVersion.getNgCliVersion().version; const commandArguments = [ 'new', @@ -84,14 +109,22 @@ const create = async(appName, options) => { '--routing=false', '--skip-tests=true', '--skip-install=true', - '--standalone=false', + '--standalone=true', '--ssr=false' ]; + if(ngCliWithZoneless.compare(currentNgVersion) <= 0) { + commandArguments.push('--zoneless=false'); + } + await runNgCommand(commandArguments, options); const appPath = path.join(process.cwd(), appName); + if(depsVersionTag) { + bumpAngular(appPath, depsVersionTag); + } + options.resolveConflicts = 'override'; options.updateBudgets = true; options.layout = layout; @@ -125,9 +158,9 @@ const changeMainTs = (appPath) => { moduleWorker.insertImport(filePath, 'devextreme/ui/themes', 'themes', true); const fileContent = fs.readFileSync(filePath).toString(); - const bootstrapPattern = /platformBrowserDynamic\(\)\.bootstrapModule\(\s*AppModule\s*(?:,\s*\{[^}]*\})?\s*\)/; + const bootstrapPattern = /bootstrapApplication\([^)]+\)/; const firstChaptStr = fileContent.match(bootstrapPattern)[0]; - const lastChaptStr = '.catch(err => console.error(err));'; + const lastChaptStr = '.catch((err) => console.error(err));'; fs.writeFileSync( filePath, diff --git a/packages/devextreme-cli/src/applications/application.nextjs.js b/packages/devextreme-cli/src/applications/application.nextjs.js new file mode 100644 index 000000000..de7b9fd63 --- /dev/null +++ b/packages/devextreme-cli/src/applications/application.nextjs.js @@ -0,0 +1,231 @@ +const runCommand = require('../utility/run-command'); +const path = require('path'); +const fs = require('fs'); +const getLayoutInfo = require('../utility/prompts/layout'); +const getTemplateTypeInfo = require('../utility/prompts/typescript'); +const templateCreator = require('../utility/template-creator'); +const packageManager = require('../utility/package-manager'); +const packageJsonUtils = require('../utility/package-json-utils'); +const insertItemToArray = require('../utility/file-content').insertItemToArray; +const stringUtils = require('../utility/string'); +const typescriptUtils = require('../utility/typescript-extension'); +const removeFile = require('../utility/file-operations').remove; +const latestVersions = require('../utility/latest-versions'); +const { extractDepsVersionTag } = require('../utility/extract-deps-version-tag'); +const { + updateJsonPropName, + bumpReact, + getCorrectPath, + addStylesToApp, + getComponentPageName, +} = require('./application.react'); + +const defaultStyles = [ + 'devextreme/dist/css/dx.light.css' +]; + +const isNextJsApp = () => { + const appPath = process.cwd(); + + return fs.existsSync(path.join(appPath, 'next.config.ts')) || fs.existsSync(path.join(appPath, 'next.config.mjs')); +}; + +const isTsApp = (appPath) => { + return fs.existsSync(path.join(appPath, 'next.config.ts')); +}; + +const getExtension = (appPath) => { + return fs.existsSync(path.join(appPath, 'src/app', 'layout.tsx')) ? '.tsx' : '.jsx'; +}; + +const pathToPagesIndex = () => { + const extension = getExtension(process.cwd()); + return path.join(process.cwd(), 'src', 'views', `index${extension}`); +}; + +const preparePackageJsonForTemplate = (appPath, appName) => { + const dependencies = [ + { name: 'devextreme-cli', version: latestVersions['devextreme-cli'], dev: true }, + { name: 'jose', version: latestVersions['jose'] }, + ]; + const scripts = [ + { name: 'build-themes', value: 'devextreme build' }, + { name: 'postinstall', value: 'npm run build-themes' } + ]; + + packageJsonUtils.addDependencies(appPath, dependencies); + packageJsonUtils.updateScripts(appPath, scripts); + packageJsonUtils.updateName(appPath, appName); +}; + +const create = async(appName, options) => { + const templateType = await getTemplateTypeInfo(options.template); + const layoutType = await getLayoutInfo(options.layout); + + const templateOptions = Object.assign({}, options, { + project: stringUtils.humanize(appName), + layout: stringUtils.classify(layoutType), + isTypeScript: typescriptUtils.isTypeScript(templateType) + }); + const depsVersionTag = extractDepsVersionTag(options); + + let commandArguments = [`-p=create-next-app@${depsVersionTag || latestVersions['create-next-app']}`, 'create-next-app', appName]; + + commandArguments = [ + ...commandArguments, + `${templateOptions.isTypeScript ? '--typescript' : '--javascript'}`, + '--eslint', + '--no-tailwind', + '--src-dir', + '--app', + '--no-turbopack', + '--import-alias "@/*"', + ]; + + await runCommand('npx', commandArguments); + + const appPath = path.join(process.cwd(), appName); + + if(depsVersionTag) { + bumpReact(appPath, depsVersionTag, templateOptions.isTypeScript); + } + + addTemplate(appPath, appName, templateOptions); + modifyAppFiles(appPath, templateOptions); +}; + +const modifyAppFiles = (appPath, { project, isTypeScript }) => { + const entryFilePath = path.join(appPath, `src/app/layout.${isTypeScript ? 'tsx' : 'jsx'}`); + + let content = fs.readFileSync(entryFilePath).toString(); + content = content.replace(/[^<]+<\/title>/, `<title>${project}<\/title>`); + + fs.writeFileSync(entryFilePath, content); +}; + +const addTemplate = (appPath, appName, templateOptions) => { + const applicationTemplatePath = path.join( + templateCreator.getTempaltePath('nextjs'), + 'application' + ); + + const manifestPath = path.join(appPath, 'public', 'manifest.json'); + + const styles = [ + '../dx-styles.scss', + '../themes/generated/theme.additional.css', + '../themes/generated/theme.additional.dark.css', + '../themes/generated/theme.base.css', + '../themes/generated/theme.base.dark.css', + 'devextreme/dist/css/dx.common.css' + ]; + + templateCreator.moveTemplateFilesToProject(applicationTemplatePath, appPath, templateOptions, getCorrectPath); + + !templateOptions.isTypeScript && removeFile(path.join(appPath, 'src', 'types.jsx')); + removeFile(path.join(appPath, 'src/app', 'page.js')); + removeFile(path.join(appPath, 'src/app', 'layout.js')); + removeFile(path.join(appPath, 'src/app', 'globals.scss')); + + if(!templateOptions.empty) { + addSamplePages(appPath, templateOptions); + } + + preparePackageJsonForTemplate(appPath, appName, templateOptions.isTypeScript); + updateJsonPropName(manifestPath, appName); + install({ isTypeScript: templateOptions.isTypeScript }, appPath, styles); +}; + +const getEntryFilePath = (options, appPath) => { + const extension = options.isTypeScript || isTsApp(appPath) ? 'ts' : 'js'; + const srcFolder = fs.existsSync(path.join(appPath, 'src')) ? 'src' : ''; + const isAppRouterApp = fs.existsSync(path.join(appPath, srcFolder, 'app')) && fs.lstatSync(appPath).isDirectory(); + + const entryFilePath = isAppRouterApp + ? path.join('app', `layout.${extension}`) + : path.join('pages', `_app.${extension}`); + + const jsx = fs.existsSync(path.join(appPath, srcFolder, entryFilePath + 'x')) ? 'x' : ''; + + return path.join(srcFolder, entryFilePath + jsx); +}; + +const install = (options, appPath, styles) => { + appPath = appPath ? appPath : process.cwd(); + + const pathToMainComponent = path.join(appPath, getEntryFilePath(options, appPath)); + + addStylesToApp(pathToMainComponent, styles || defaultStyles); + packageJsonUtils.addDevextreme(appPath, options.dxversion, 'react'); + + packageManager.runInstall({ cwd: appPath }); +}; + +const getNavigationData = (viewName, componentName, icon) => { + const pagePath = stringUtils.dasherize(viewName); + return { + navigation: `\n {\n text: \'${stringUtils.humanize(viewName)}\',\n path: \'/pages/${pagePath}\',\n icon: \'${icon}\'\n }` + }; +}; + +const createPathToPage = (pageName) => { + const pagesPath = path.join(process.cwd(), 'src', 'app/pages'); + const newPageFolderPath = path.join(pagesPath, pageName); + + if(!fs.existsSync(pagesPath)) { + fs.mkdirSync(pagesPath); + fs.writeFileSync(pathToPagesIndex(), ''); + } + + if(!fs.existsSync(newPageFolderPath)) { + fs.mkdirSync(newPageFolderPath); + } + + return newPageFolderPath; +}; + +const addSamplePages = (appPath, templateOptions) => { + const samplePageTemplatePath = path.join( + templateCreator.getTempaltePath('nextjs'), + 'sample-pages' + ); + + const pagesPath = path.join(appPath, 'src', 'app/pages'); + + templateCreator.moveTemplateFilesToProject(samplePageTemplatePath, pagesPath, { + isTypeScript: templateOptions.isTypeScript + }, getCorrectPath); +}; + +const addView = (pageName, options) => { + const pageTemplatePath = path.join( + templateCreator.getTempaltePath('nextjs'), + 'page' + ); + const extension = getExtension(process.cwd()); + + const componentName = getComponentPageName(pageName); + const pathToPage = createPathToPage(pageName); + const navigationModulePath = path.join(process.cwd(), 'src', `app-navigation${extension}`); + const navigationData = getNavigationData(pageName, componentName, options && options.icon || 'folder'); + + const getCorrectExtension = (fileExtension) => { + return fileExtension === '.tsx' ? extension : fileExtension; + }; + + const getPageFileName = (pageName, pageItem) => { + return pageItem === 'page.tsx' ? 'page' : pageName; + }; + + templateCreator.addPageToApp(pageName, pathToPage, pageTemplatePath, getCorrectExtension, { getPageFileName }); + + insertItemToArray(navigationModulePath, navigationData.navigation); +}; + +module.exports = { + isNextJsApp, + install, + create, + addTemplate, + addView +}; diff --git a/packages/devextreme-cli/src/applications/application.react.js b/packages/devextreme-cli/src/applications/application.react.js index 9be907fa6..1b3f8c36c 100644 --- a/packages/devextreme-cli/src/applications/application.react.js +++ b/packages/devextreme-cli/src/applications/application.react.js @@ -3,6 +3,7 @@ const path = require('path'); const fs = require('fs'); const getLayoutInfo = require('../utility/prompts/layout'); const getTemplateTypeInfo = require('../utility/prompts/typescript'); +const getTranspilerTypeInfo = require('../utility/prompts/transpiler'); const templateCreator = require('../utility/template-creator'); const packageManager = require('../utility/package-manager'); const packageJsonUtils = require('../utility/package-json-utils'); @@ -13,13 +14,13 @@ const stringUtils = require('../utility/string'); const typescriptUtils = require('../utility/typescript-extension'); const removeFile = require('../utility/file-operations').remove; const latestVersions = require('../utility/latest-versions'); -const { extractToolingVersion } = require('../utility/extract-tooling-version'); +const { extractDepsVersionTag } = require('../utility/extract-deps-version-tag'); const defaultStyles = [ 'devextreme/dist/css/dx.light.css' ]; const getExtension = (appPath) => { - return fs.existsSync(path.join(appPath, 'src', 'App.tsx')) ? '.tsx' : '.js'; + return fs.existsSync(path.join(appPath, 'src', 'App.tsx')) ? '.tsx' : '.jsx'; }; const pathToPagesIndex = () => { @@ -27,9 +28,9 @@ const pathToPagesIndex = () => { return path.join(process.cwd(), 'src', 'pages', `index${extension}`); }; -const preparePackageJsonForTemplate = (appPath, appName, isTypeScript) => { +const preparePackageJsonForTemplate = (appPath, appName) => { const dependencies = [ - { name: 'sass', version: '^1.34.1' }, + { name: 'sass-embedded', version: '^1.85.1' }, { name: 'devextreme-cli', version: latestVersions['devextreme-cli'], dev: true }, { name: 'react-router-dom', version: '^6.3.0' }, ]; @@ -51,8 +52,25 @@ const updateJsonPropName = (path, name) => { }); }; +const bumpReact = (appPath, versionTag, isTypeScript) => { + const dependencies = [ + { name: 'react', version: versionTag }, + { name: 'react-dom', version: versionTag }, + ]; + + if(isTypeScript) { + dependencies.push( + { name: '@types/react', version: versionTag, dev: true }, + { name: '@types/react-dom', version: versionTag, dev: true }, + ); + } + + packageJsonUtils.addDependencies(appPath, dependencies); +}; + const create = async(appName, options) => { const templateType = await getTemplateTypeInfo(options.template); + const transpiler = await getTranspilerTypeInfo(options.transpiler); const layoutType = await getLayoutInfo(options.layout); const templateOptions = Object.assign({}, options, { @@ -60,27 +78,30 @@ const create = async(appName, options) => { layout: stringUtils.classify(layoutType), isTypeScript: typescriptUtils.isTypeScript(templateType) }); - const toolingVersion = extractToolingVersion(options); - const commandArguments = [`-p=create-react-app${toolingVersion}`, 'create-react-app', appName]; + const depsVersionTag = extractDepsVersionTag(options); - const templateSuffix = templateOptions.isTypeScript ? '-typescript' : ''; - const templatePath = path.resolve(__dirname, `../templates/cra-template${templateSuffix}`); + const commandArguments = [`-p=create-vite@${depsVersionTag || latestVersions['create-vite']}`, 'create-vite', appName]; - commandArguments.push(`--template file:${templatePath}`); + commandArguments.push(`--template react${transpiler === 'swc' ? '-swc' : ''}${templateOptions.isTypeScript ? '-ts' : ''}`); await runCommand('npx', commandArguments); const appPath = path.join(process.cwd(), appName); modifyIndexHtml(appPath, templateOptions.project); + + if(depsVersionTag) { + bumpReact(appPath, depsVersionTag, templateOptions.isTypeScript); + } + addTemplate(appPath, appName, templateOptions); }; const modifyIndexHtml = (appPath, appName) => { - const indexHtmlPath = path.join(appPath, 'public', 'index.html'); + const indexHtmlPath = path.join(appPath, 'index.html'); let htmlContent = fs.readFileSync(indexHtmlPath).toString(); - htmlContent = htmlContent.replace(/<title>(\w+\s*)+<\/title>/, `<title>${appName}<\/title>`); + htmlContent = htmlContent.replace(/<title>[^<]+<\/title>/, `<title>${appName}<\/title>`); htmlContent = htmlContent.replace('<body>', '<body class="dx-viewport">'); fs.writeFileSync(indexHtmlPath, htmlContent); @@ -97,7 +118,6 @@ const addTemplate = (appPath, appName, templateOptions) => { ); const manifestPath = path.join(appPath, 'public', 'manifest.json'); - const indexPath = path.join(appPath, 'src', templateOptions.isTypeScript ? 'index.tsx' : 'index.js'); const styles = [ './themes/generated/theme.additional.css', @@ -108,15 +128,15 @@ const addTemplate = (appPath, appName, templateOptions) => { ]; templateCreator.moveTemplateFilesToProject(applicationTemplatePath, appPath, templateOptions, getCorrectPath); - removeFile(path.join(appPath, 'src', 'App.css')); - !templateOptions.isTypeScript && removeFile(path.join(appPath, 'src', 'types.js')); + + !templateOptions.isTypeScript && removeFile(path.join(appPath, 'src', 'types.jsx')); + if(!templateOptions.empty) { addSamplePages(appPath, templateOptions); } preparePackageJsonForTemplate(appPath, appName, templateOptions.isTypeScript); updateJsonPropName(manifestPath, appName); - addPolyfills(packageJsonUtils.getPackageJsonPath(), indexPath); install({}, appPath, styles); }; @@ -130,15 +150,6 @@ const install = (options, appPath, styles) => { packageManager.runInstall({ cwd: appPath }); }; -const addPolyfills = (packagePath, indexPath) => { - const packages = [ - { name: 'react-app-polyfill', version: '^1.0.0' } - ]; - - packageJsonUtils.addDependencies(packagePath, packages); - moduleUtils.insertImport(indexPath, './polyfills'); -}; - const addStylesToApp = (filePath, styles) => { styles.forEach(style => { moduleUtils.insertImport(filePath, style); @@ -213,5 +224,10 @@ module.exports = { install, create, addTemplate, - addView + addView, + updateJsonPropName, + bumpReact, + getCorrectPath, + addStylesToApp, + getComponentPageName, }; diff --git a/packages/devextreme-cli/src/applications/application.vue.js b/packages/devextreme-cli/src/applications/application.vue.js index 0f4890b0c..9d02f04fa 100644 --- a/packages/devextreme-cli/src/applications/application.vue.js +++ b/packages/devextreme-cli/src/applications/application.vue.js @@ -9,7 +9,7 @@ const insertItemToArray = require('../utility/file-content').insertItemToArray; const moduleUtils = require('../utility/module'); const stringUtils = require('../utility/string'); const latestVersions = require('../utility/latest-versions'); -const { toolingVersionOptionName, extractToolingVersion } = require('../utility/extract-tooling-version'); +const { depsVersionTagOptionName, extractDepsVersionTag } = require('../utility/extract-deps-version-tag'); const defaultStyles = [ 'devextreme/dist/css/dx.light.css' ]; @@ -37,25 +37,42 @@ const preparePackageJsonForTemplate = (appPath, appName) => { packageJsonUtils.updateName(appPath, appName); }; -async function createVueApp(name, templateOptions) { - const toolingVersion = extractToolingVersion(templateOptions); - const argList = ['-p', `@vue/cli${toolingVersion}`, 'vue', 'create', name, '--registry', 'https://registry.npmjs.org/', '-p "Default (Vue 3)"']; +async function createVueApp(name, depsVersionTag) { + const argList = ['-p', `@vue/cli@${depsVersionTag}`, 'vue', 'create', name, '--registry', 'https://registry.npmjs.org/', '-p "Default (Vue 3)"']; return runCommand('npx', argList); } +const bumpVue = (appPath, versionTag) => { + const dependencies = [ + { name: 'vue', version: versionTag }, + { name: 'vue-router', version: versionTag }, + { name: '@vue/cli-plugin-babel', version: versionTag, dev: true }, + { name: '@vue/cli-plugin-eslint', version: versionTag, dev: true }, + { name: '@vue/cli-service', version: versionTag, dev: true }, + ]; + + packageJsonUtils.addDependencies(appPath, dependencies); +}; + const create = async(appName, options) => { const layout = await getLayoutInfo(options.layout); + const depsVersionTag = extractDepsVersionTag(options); const templateOptions = { project: stringUtils.humanize(appName), layout: layout, - [toolingVersionOptionName]: options[toolingVersionOptionName] + [depsVersionTagOptionName]: options[depsVersionTagOptionName] }; - await createVueApp(appName, templateOptions); + await createVueApp(appName, depsVersionTag); const appPath = path.join(process.cwd(), appName); + + if(depsVersionTag) { + bumpVue(appPath, depsVersionTag); + } + modifyIndexHtml(appPath, templateOptions.project); addTemplate(appPath, appName, templateOptions); }; diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/LICENSE b/packages/devextreme-cli/src/templates/cra-template-typescript/LICENSE deleted file mode 100644 index 5930f2b8d..000000000 --- a/packages/devextreme-cli/src/templates/cra-template-typescript/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2013-present, Facebook, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/README.md b/packages/devextreme-cli/src/templates/cra-template-typescript/README.md deleted file mode 100644 index e7197568d..000000000 --- a/packages/devextreme-cli/src/templates/cra-template-typescript/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# cra-template-typescript - -This is the official TypeScript template for [Create React App](https://github.com/facebook/create-react-app). - -To use this template, add `--template typescript` when creating a new app. - -For example: - -```sh -npx create-react-app my-app --template typescript - -# or - -yarn create react-app my-app --template typescript -``` - -For more information, please refer to: - -- [Getting Started](https://create-react-app.dev/docs/getting-started) – How to create a new app. -- [User Guide](https://create-react-app.dev) – How to develop apps bootstrapped with Create React App. diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/package.json b/packages/devextreme-cli/src/templates/cra-template-typescript/package.json deleted file mode 100644 index 88de656d7..000000000 --- a/packages/devextreme-cli/src/templates/cra-template-typescript/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "cra-template-typescript", - "version": "1.2.0", - "keywords": [ - "react", - "create-react-app", - "template", - "typescript" - ], - "description": "The base TypeScript template for Create React App.", - "repository": { - "type": "git", - "url": "https://github.com/facebook/create-react-app.git", - "directory": "packages/cra-template-typescript" - }, - "license": "MIT", - "engines": { - "node": ">=14.21.3" - }, - "bugs": { - "url": "https://github.com/facebook/create-react-app/issues" - }, - "files": [ - "template", - "template.json" - ] -} diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/template.json b/packages/devextreme-cli/src/templates/cra-template-typescript/template.json deleted file mode 100644 index 3678a922d..000000000 --- a/packages/devextreme-cli/src/templates/cra-template-typescript/template.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "package": { - "dependencies": { - "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.2.0", - "@testing-library/dom": "^10.4.0", - "@testing-library/user-event": "^14.6.0", - "@types/jest": "^29.5.14", - "@types/node": "^22.0.0", - "@types/react": "^19.0.0", - "@types/react-dom": "^19.0.0", - "typescript": "^4.0.0", - "web-vitals": "^4.2.4" - }, - "eslintConfig": { - "extends": ["react-app", "react-app/jest"] - } - } -} diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/template/README.md b/packages/devextreme-cli/src/templates/cra-template-typescript/template/README.md deleted file mode 100644 index b87cb0044..000000000 --- a/packages/devextreme-cli/src/templates/cra-template-typescript/template/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# Getting Started with Create React App - -This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). - -## Available Scripts - -In the project directory, you can run: - -### `npm start` - -Runs the app in the development mode.\ -Open [http://localhost:3000](http://localhost:3000) to view it in the browser. - -The page will reload if you make edits.\ -You will also see any lint errors in the console. - -### `npm test` - -Launches the test runner in the interactive watch mode.\ -See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. - -### `npm run build` - -Builds the app for production to the `build` folder.\ -It correctly bundles React in production mode and optimizes the build for the best performance. - -The build is minified and the filenames include the hashes.\ -Your app is ready to be deployed! - -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. - -### `npm run eject` - -**Note: this is a one-way operation. Once you `eject`, you can’t go back!** - -If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. - -Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. - -You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. - -## Learn More - -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). - -To learn React, check out the [React documentation](https://reactjs.org/). diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/template/gitignore b/packages/devextreme-cli/src/templates/cra-template-typescript/template/gitignore deleted file mode 100644 index 4d29575de..000000000 --- a/packages/devextreme-cli/src/templates/cra-template-typescript/template/gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/template/public/index.html b/packages/devextreme-cli/src/templates/cra-template-typescript/template/public/index.html deleted file mode 100644 index aa069f27c..000000000 --- a/packages/devextreme-cli/src/templates/cra-template-typescript/template/public/index.html +++ /dev/null @@ -1,43 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="utf-8" /> - <link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> - <meta name="viewport" content="width=device-width, initial-scale=1" /> - <meta name="theme-color" content="#000000" /> - <meta - name="description" - content="Web site created using create-react-app" - /> - <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> - <!-- - manifest.json provides metadata used when your web app is installed on a - user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ - --> - <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> - <!-- - Notice the use of %PUBLIC_URL% in the tags above. - It will be replaced with the URL of the `public` folder during the build. - Only files inside the `public` folder can be referenced from the HTML. - - Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will - work correctly both with client-side routing and a non-root public URL. - Learn how to configure a non-root public URL by running `npm run build`. - --> - <title>React App - - - -
- - - diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/App.css b/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/App.css deleted file mode 100644 index 74b5e0534..000000000 --- a/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/App.test.tsx b/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/App.test.tsx deleted file mode 100644 index f0db7d3d4..000000000 --- a/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/App.test.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import { screen } from '@testing-library/dom'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/App.tsx b/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/App.tsx deleted file mode 100644 index a53698aab..000000000 --- a/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/App.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import logo from './logo.svg'; -import './App.css'; - -function App() { - return ( -
-
- logo -

- Edit src/App.tsx and save to reload. -

- - Learn React - -
-
- ); -} - -export default App; diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/index.tsx b/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/index.tsx deleted file mode 100644 index 032464fb6..000000000 --- a/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/index.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; - -const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLElement -); -root.render( - - - -); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/logo.svg b/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/logo.svg deleted file mode 100644 index 9dfc1c058..000000000 --- a/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/reportWebVitals.ts b/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/reportWebVitals.ts deleted file mode 100644 index 1e8febd4a..000000000 --- a/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/reportWebVitals.ts +++ /dev/null @@ -1,13 +0,0 @@ -const reportWebVitals = (onPerfEntry?: () => any) => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ onLCP, onINP, onCLS, onFCP, onTTFB }) => { - onCLS(onPerfEntry); - onINP(onPerfEntry); - onLCP(onPerfEntry); - onFCP(onPerfEntry); - onTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/setupTests.ts b/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/setupTests.ts deleted file mode 100644 index 8f2609b7b..000000000 --- a/packages/devextreme-cli/src/templates/cra-template-typescript/template/src/setupTests.ts +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; diff --git a/packages/devextreme-cli/src/templates/cra-template/LICENSE b/packages/devextreme-cli/src/templates/cra-template/LICENSE deleted file mode 100644 index 5930f2b8d..000000000 --- a/packages/devextreme-cli/src/templates/cra-template/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2013-present, Facebook, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/packages/devextreme-cli/src/templates/cra-template/README.md b/packages/devextreme-cli/src/templates/cra-template/README.md deleted file mode 100644 index 9e8434b78..000000000 --- a/packages/devextreme-cli/src/templates/cra-template/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# cra-template - -This is the official base template for [Create React App](https://github.com/facebook/create-react-app). - -If you don't specify a template (for example, `--template typescript`), this template will be used by default. - -For more information, please refer to: - -- [Getting Started](https://create-react-app.dev/docs/getting-started) – How to create a new app. -- [User Guide](https://create-react-app.dev) – How to develop apps bootstrapped with Create React App. diff --git a/packages/devextreme-cli/src/templates/cra-template/package.json b/packages/devextreme-cli/src/templates/cra-template/package.json deleted file mode 100644 index 53350728e..000000000 --- a/packages/devextreme-cli/src/templates/cra-template/package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "name": "cra-template", - "version": "1.2.0", - "keywords": [ - "react", - "create-react-app", - "template" - ], - "description": "The base template for Create React App.", - "repository": { - "type": "git", - "url": "https://github.com/facebook/create-react-app.git", - "directory": "packages/cra-template" - }, - "license": "MIT", - "engines": { - "node": ">=14.21.3" - }, - "bugs": { - "url": "https://github.com/facebook/create-react-app/issues" - }, - "files": [ - "template", - "template.json" - ] -} diff --git a/packages/devextreme-cli/src/templates/cra-template/template.json b/packages/devextreme-cli/src/templates/cra-template/template.json deleted file mode 100644 index 65327dcb1..000000000 --- a/packages/devextreme-cli/src/templates/cra-template/template.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "package": { - "dependencies": { - "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.2.0", - "@testing-library/dom": "^10.4.0", - "@testing-library/user-event": "^14.6.0", - "web-vitals": "^4.2.4" - }, - "eslintConfig": { - "extends": ["react-app", "react-app/jest"] - } - } -} diff --git a/packages/devextreme-cli/src/templates/cra-template/template/README.md b/packages/devextreme-cli/src/templates/cra-template/template/README.md deleted file mode 100644 index 58beeaccd..000000000 --- a/packages/devextreme-cli/src/templates/cra-template/template/README.md +++ /dev/null @@ -1,70 +0,0 @@ -# Getting Started with Create React App - -This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). - -## Available Scripts - -In the project directory, you can run: - -### `npm start` - -Runs the app in the development mode.\ -Open [http://localhost:3000](http://localhost:3000) to view it in your browser. - -The page will reload when you make changes.\ -You may also see any lint errors in the console. - -### `npm test` - -Launches the test runner in the interactive watch mode.\ -See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. - -### `npm run build` - -Builds the app for production to the `build` folder.\ -It correctly bundles React in production mode and optimizes the build for the best performance. - -The build is minified and the filenames include the hashes.\ -Your app is ready to be deployed! - -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. - -### `npm run eject` - -**Note: this is a one-way operation. Once you `eject`, you can't go back!** - -If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. - -Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. - -You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. - -## Learn More - -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). - -To learn React, check out the [React documentation](https://reactjs.org/). - -### Code Splitting - -This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) - -### Analyzing the Bundle Size - -This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) - -### Making a Progressive Web App - -This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) - -### Advanced Configuration - -This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) - -### Deployment - -This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) - -### `npm run build` fails to minify - -This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) diff --git a/packages/devextreme-cli/src/templates/cra-template/template/gitignore b/packages/devextreme-cli/src/templates/cra-template/template/gitignore deleted file mode 100644 index 4d29575de..000000000 --- a/packages/devextreme-cli/src/templates/cra-template/template/gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.js - -# testing -/coverage - -# production -/build - -# misc -.DS_Store -.env.local -.env.development.local -.env.test.local -.env.production.local - -npm-debug.log* -yarn-debug.log* -yarn-error.log* diff --git a/packages/devextreme-cli/src/templates/cra-template/template/public/favicon.ico b/packages/devextreme-cli/src/templates/cra-template/template/public/favicon.ico deleted file mode 100644 index a11777cc4..000000000 Binary files a/packages/devextreme-cli/src/templates/cra-template/template/public/favicon.ico and /dev/null differ diff --git a/packages/devextreme-cli/src/templates/cra-template/template/public/index.html b/packages/devextreme-cli/src/templates/cra-template/template/public/index.html deleted file mode 100644 index aa069f27c..000000000 --- a/packages/devextreme-cli/src/templates/cra-template/template/public/index.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - React App - - - -
- - - diff --git a/packages/devextreme-cli/src/templates/cra-template/template/src/App.css b/packages/devextreme-cli/src/templates/cra-template/template/src/App.css deleted file mode 100644 index 74b5e0534..000000000 --- a/packages/devextreme-cli/src/templates/cra-template/template/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/packages/devextreme-cli/src/templates/cra-template/template/src/App.js b/packages/devextreme-cli/src/templates/cra-template/template/src/App.js deleted file mode 100644 index 378457572..000000000 --- a/packages/devextreme-cli/src/templates/cra-template/template/src/App.js +++ /dev/null @@ -1,25 +0,0 @@ -import logo from './logo.svg'; -import './App.css'; - -function App() { - return ( -
-
- logo -

- Edit src/App.js and save to reload. -

- - Learn React - -
-
- ); -} - -export default App; diff --git a/packages/devextreme-cli/src/templates/cra-template/template/src/App.test.js b/packages/devextreme-cli/src/templates/cra-template/template/src/App.test.js deleted file mode 100644 index 23b8367e5..000000000 --- a/packages/devextreme-cli/src/templates/cra-template/template/src/App.test.js +++ /dev/null @@ -1,9 +0,0 @@ -import { render } from '@testing-library/react'; -import { screen } from '@testing-library/dom'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/packages/devextreme-cli/src/templates/cra-template/template/src/index.js b/packages/devextreme-cli/src/templates/cra-template/template/src/index.js deleted file mode 100644 index d563c0fb1..000000000 --- a/packages/devextreme-cli/src/templates/cra-template/template/src/index.js +++ /dev/null @@ -1,17 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; - -const root = ReactDOM.createRoot(document.getElementById('root')); -root.render( - - - -); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/packages/devextreme-cli/src/templates/cra-template/template/src/logo.svg b/packages/devextreme-cli/src/templates/cra-template/template/src/logo.svg deleted file mode 100644 index 9dfc1c058..000000000 --- a/packages/devextreme-cli/src/templates/cra-template/template/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/packages/devextreme-cli/src/templates/cra-template/template/src/reportWebVitals.js b/packages/devextreme-cli/src/templates/cra-template/template/src/reportWebVitals.js deleted file mode 100644 index 3c0ccb0dc..000000000 --- a/packages/devextreme-cli/src/templates/cra-template/template/src/reportWebVitals.js +++ /dev/null @@ -1,13 +0,0 @@ -const reportWebVitals = onPerfEntry => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ onLCP, onINP, onCLS, onFCP, onTTFB }) => { - onCLS(onPerfEntry); - onINP(onPerfEntry); - onLCP(onPerfEntry); - onFCP(onPerfEntry); - onTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; diff --git a/packages/devextreme-cli/src/templates/cra-template/template/src/setupTests.js b/packages/devextreme-cli/src/templates/cra-template/template/src/setupTests.js deleted file mode 100644 index 8f2609b7b..000000000 --- a/packages/devextreme-cli/src/templates/cra-template/template/src/setupTests.js +++ /dev/null @@ -1,5 +0,0 @@ -// jest-dom adds custom jest matchers for asserting on DOM nodes. -// allows you to do things like: -// expect(element).toHaveTextContent(/react/i) -// learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; diff --git a/packages/devextreme-cli/src/templates/nextjs/application/.env b/packages/devextreme-cli/src/templates/nextjs/application/.env new file mode 100644 index 000000000..1d70c522d --- /dev/null +++ b/packages/devextreme-cli/src/templates/nextjs/application/.env @@ -0,0 +1 @@ +SESSION_SECRET= \ No newline at end of file diff --git a/packages/devextreme-cli/src/templates/nextjs/application/devextreme.json b/packages/devextreme-cli/src/templates/nextjs/application/devextreme.json new file mode 100644 index 000000000..b6bf64dac --- /dev/null +++ b/packages/devextreme-cli/src/templates/nextjs/application/devextreme.json @@ -0,0 +1,63 @@ +{ + "applicationEngine": "nextjs", + "build": { + "commands": [ + { + "command": "build-theme", + "options": { + "inputFile": "src/themes/metadata.base.json", + "outputFile": "src/themes/generated/theme.base.css" + } + }, + { + "command": "build-theme", + "options": { + "inputFile": "src/themes/metadata.base.dark.json", + "outputFile": "src/themes/generated/theme.base.dark.css" + } + }, + { + "command": "build-theme", + "options": { + "inputFile": "src/themes/metadata.additional.json", + "outputFile": "src/themes/generated/theme.additional.css" + } + }, + { + "command": "build-theme", + "options": { + "inputFile": "src/themes/metadata.additional.dark.json", + "outputFile": "src/themes/generated/theme.additional.dark.css" + } + }, + { + "command": "export-theme-vars", + "options": { + "inputFile": "src/themes/metadata.base.json", + "outputFile": "src/themes/generated/variables.base.scss" + } + }, + { + "command": "export-theme-vars", + "options": { + "inputFile": "src/themes/metadata.base.dark.json", + "outputFile": "src/themes/generated/variables.base.dark.scss" + } + }, + { + "command": "export-theme-vars", + "options": { + "inputFile": "src/themes/metadata.additional.json", + "outputFile": "src/themes/generated/variables.additional.scss" + } + }, + { + "command": "export-theme-vars", + "options": { + "inputFile": "src/themes/metadata.additional.dark.json", + "outputFile": "src/themes/generated/variables.additional.dark.scss" + } + } + ] + } +} diff --git a/packages/devextreme-cli/src/templates/nextjs/application/next.config.mjs b/packages/devextreme-cli/src/templates/nextjs/application/next.config.mjs new file mode 100644 index 000000000..377a4d1eb --- /dev/null +++ b/packages/devextreme-cli/src/templates/nextjs/application/next.config.mjs @@ -0,0 +1,32 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + async redirects() { + return [ + { + source: '/', + destination: '/pages/home', + permanent: true, + }, + { + source: '/login', + destination: '/auth/login', + permanent: true, + }, + { + source: '/reset-password', + destination: '/auth/reset-password', + permanent: true, + }, + { + source: '/create-account', + destination: '/auth/create-account', + permanent: true, + }, + ] + }, + images: { + remotePatterns: [new URL('https://js.devexpress.com/**')] + }, +} + +export default nextConfig; diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/template/public/logo192.png b/packages/devextreme-cli/src/templates/nextjs/application/public/logo192.png similarity index 100% rename from packages/devextreme-cli/src/templates/cra-template-typescript/template/public/logo192.png rename to packages/devextreme-cli/src/templates/nextjs/application/public/logo192.png diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/template/public/logo512.png b/packages/devextreme-cli/src/templates/nextjs/application/public/logo512.png similarity index 100% rename from packages/devextreme-cli/src/templates/cra-template-typescript/template/public/logo512.png rename to packages/devextreme-cli/src/templates/nextjs/application/public/logo512.png diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/template/public/manifest.json b/packages/devextreme-cli/src/templates/nextjs/application/public/manifest.json similarity index 100% rename from packages/devextreme-cli/src/templates/cra-template-typescript/template/public/manifest.json rename to packages/devextreme-cli/src/templates/nextjs/application/public/manifest.json diff --git a/packages/devextreme-cli/src/templates/cra-template-typescript/template/public/robots.txt b/packages/devextreme-cli/src/templates/nextjs/application/public/robots.txt similarity index 100% rename from packages/devextreme-cli/src/templates/cra-template-typescript/template/public/robots.txt rename to packages/devextreme-cli/src/templates/nextjs/application/public/robots.txt diff --git a/packages/devextreme-cli/src/templates/nextjs/application/src/app-info.tsx b/packages/devextreme-cli/src/templates/nextjs/application/src/app-info.tsx new file mode 100644 index 000000000..9e15c1476 --- /dev/null +++ b/packages/devextreme-cli/src/templates/nextjs/application/src/app-info.tsx @@ -0,0 +1,5 @@ +const appInfo = { + title: '<%=project%>' +}; +export default appInfo; + diff --git a/packages/devextreme-cli/src/templates/nextjs/application/src/app-navigation.tsx b/packages/devextreme-cli/src/templates/nextjs/application/src/app-navigation.tsx new file mode 100644 index 000000000..8e39088af --- /dev/null +++ b/packages/devextreme-cli/src/templates/nextjs/application/src/app-navigation.tsx @@ -0,0 +1,21 @@ +export const navigation = [<%=^empty%> + { + text: 'Home', + path: '/pages/home', + icon: 'home' + }, + { + text: 'Examples', + icon: 'folder', + items: [ + { + text: 'Profile', + path: '/pages/profile' + }, + { + text: 'Tasks', + path: '/pages/tasks' + } + ] + } + <%=/empty%>]; diff --git a/packages/devextreme-cli/src/templates/nextjs/application/src/app/actions/auth.ts b/packages/devextreme-cli/src/templates/nextjs/application/src/app/actions/auth.ts new file mode 100644 index 000000000..f16cafb86 --- /dev/null +++ b/packages/devextreme-cli/src/templates/nextjs/application/src/app/actions/auth.ts @@ -0,0 +1,76 @@ +'use server' +import { redirect } from 'next/navigation' +import defaultUser from '@/utils/default-user'; +import { createSession, deleteSession } from '@/app/lib/session' + +export async function signUp(email<%=#isTypeScript%>: string<%=/isTypeScript%>, password<%=#isTypeScript%>: string<%=/isTypeScript%>) { + try { + // 1. Check if the user exists in the database and return isOk: false if so; + // 2. Otherwise, add the user to the database. + console.log(email, password); + + return { + isOk: true, + } + } catch { + return { + isOk: false, + message: 'Unable to create an account', + } + } +} + +export async function signIn(email<%=#isTypeScript%>: string<%=/isTypeScript%>, password<%=#isTypeScript%>: string<%=/isTypeScript%>) { + try { + // Verify that a user exists + console.log(email, password); + + await createSession(defaultUser.id); + + return { + isOk: true, + } + } catch { + return { + isOk: false, + message: 'Unable to sign in', + } + } +} + +export async function signOut() { + await deleteSession(); + redirect('/login'); +} + +export async function changePassword(email<%=#isTypeScript%>: string<%=/isTypeScript%>, recoveryCode<%=#isTypeScript%>?: string<%=/isTypeScript%>) { + try { + // Verify the recovery code + console.log(email, recoveryCode); + + return { + isOk: true, + } + } catch { + return { + isOk: false, + message: 'Unable to change the password', + } + } +} + +export async function resetPassword(email<%=#isTypeScript%>: string<%=/isTypeScript%>) { + try { + // Reset password + console.log(email); + + return { + isOk: true, + } + } catch { + return { + isOk: false, + message: 'Unable to reset password', + } + } +} diff --git a/packages/devextreme-cli/src/templates/nextjs/application/src/app/auth/[type]/page.tsx b/packages/devextreme-cli/src/templates/nextjs/application/src/app/auth/[type]/page.tsx new file mode 100644 index 000000000..a208cc522 --- /dev/null +++ b/packages/devextreme-cli/src/templates/nextjs/application/src/app/auth/[type]/page.tsx @@ -0,0 +1,49 @@ +'use client' +import { use } from 'react'; +import { notFound } from 'next/navigation'; +import { SingleCard } from '@/layouts'; +import { + LoginForm, + CreateAccountForm, + ResetPasswordForm, + ChangePasswordForm, +} from '@/components'; + +const formText<%=#isTypeScript%>: Record><%=/isTypeScript%> = { + 'login': { + title: 'Sign In' + }, + 'create-account': { + title: 'Sign Up' + }, + 'reset-password': { + title: 'Reset Password', + description: 'Please enter the email address that you used to register, and we will send you a link to reset your password via Email.' + }, + 'change-password': { + title: 'Change Password', + } +} + +function AuthForm({name}<%=#isTypeScript%>: {name: string}<%=/isTypeScript%>) { + switch (name) { + case 'login': return ; + case 'create-account': return ; + case 'reset-password': return ; + case 'change-password': return ; + } +} + +export default function AuthPage({ params }<%=#isTypeScript%>: {params: Promise<{type: string}>}<%=/isTypeScript%>) { + const { type } = use(params) + + if (!formText[type]) { + notFound(); + } + + const { title, description } = formText[type]; + + return + + +} diff --git a/packages/devextreme-cli/src/templates/nextjs/application/src/app/layout.tsx b/packages/devextreme-cli/src/templates/nextjs/application/src/app/layout.tsx new file mode 100644 index 000000000..daaaf4484 --- /dev/null +++ b/packages/devextreme-cli/src/templates/nextjs/application/src/app/layout.tsx @@ -0,0 +1,17 @@ +<%=#isTypeScript%>import type { PropsWithChildren } from 'react'; +<%=/isTypeScript%>import { ThemeProvider } from "@/theme"; + +export default function RootLayout({ children }<%=#isTypeScript%>: PropsWithChildren<%=/isTypeScript%>) { + return ( + + NextJs Dx App + + +
+ {children} +
+
+ + + ); +} diff --git a/packages/devextreme-cli/src/templates/nextjs/application/src/app/lib/session.ts b/packages/devextreme-cli/src/templates/nextjs/application/src/app/lib/session.ts new file mode 100644 index 000000000..2e3e5df69 --- /dev/null +++ b/packages/devextreme-cli/src/templates/nextjs/application/src/app/lib/session.ts @@ -0,0 +1,47 @@ +import 'server-only'; +import { SignJWT, jwtVerify } from 'jose'; +import { cookies } from 'next/headers'; +<%=#isTypeScript%>import type { SessionPayload } from '@/types'; +<%=/isTypeScript%> +const secretKey = process.env.SESSION_SECRET; +const encoder = new TextEncoder(); +const encodedKey = encoder.encode(secretKey); + +export async function encrypt(payload<%=#isTypeScript%>: SessionPayload<%=/isTypeScript%>) { + return new SignJWT(payload) + .setProtectedHeader({ alg: 'HS256' }) + .setIssuedAt() + .setExpirationTime('7d') + .sign(encodedKey); +} + +export async function decrypt(session<%=#isTypeScript%>: string | undefined = ''<%=/isTypeScript%>) { + try { + const { payload } = await jwtVerify(session, encodedKey, { + algorithms: ['HS256'], + }); + + return payload; + } catch { + console.log('Failed to verify session'); + } +} + +export async function createSession(userId<%=#isTypeScript%>: string<%=/isTypeScript%>) { + const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); + const session = await encrypt({ userId, expiresAt }); + const cookieStore = await cookies(); + + cookieStore.set('session', session, { + httpOnly: true, + secure: true, + expires: expiresAt, + sameSite: 'lax', + path: '/', + }); +} + +export async function deleteSession() { + const cookieStore = await cookies(); + cookieStore.delete('session'); +} diff --git a/packages/devextreme-cli/src/templates/nextjs/application/src/app/pages/layout.tsx b/packages/devextreme-cli/src/templates/nextjs/application/src/app/pages/layout.tsx new file mode 100644 index 000000000..12cb05321 --- /dev/null +++ b/packages/devextreme-cli/src/templates/nextjs/application/src/app/pages/layout.tsx @@ -0,0 +1,18 @@ +<%=#isTypeScript%>import type { PropsWithChildren } from 'react'; +<%=/isTypeScript%>import appInfo from '@/app-info'; +import { Footer } from '@/components'; +import { <%=layout%> as SideNavBarLayout } from '@/layouts'; + +export default function Content({children}<%=#isTypeScript%>: PropsWithChildren<%=/isTypeScript%>) { + return ( + + {children} +
+ Copyright © 2011-{new Date().getFullYear()} {appInfo.title} Inc. +
+ All trademarks or registered trademarks are property of their + respective owners. +
+
+ ); +} diff --git a/packages/devextreme-cli/src/templates/nextjs/application/src/components/change-password-form/ChangePasswordForm.tsx b/packages/devextreme-cli/src/templates/nextjs/application/src/components/change-password-form/ChangePasswordForm.tsx new file mode 100644 index 000000000..6c779f650 --- /dev/null +++ b/packages/devextreme-cli/src/templates/nextjs/application/src/components/change-password-form/ChangePasswordForm.tsx @@ -0,0 +1,86 @@ +'use client' +import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useRef, useCallback } from 'react'; +import { useRouter } from 'next/navigation'; +import Form, { + Item, + Label, + ButtonItem, + ButtonOptions, + RequiredRule, + CustomRule, +} from 'devextreme-react/form'; +import LoadIndicator from 'devextreme-react/load-indicator'; +import notify from 'devextreme/ui/notify'; +<%=#isTypeScript%>import { ValidationCallbackData } from 'devextreme-react/common';<%=/isTypeScript%> +import { changePassword } from '@/app/actions/auth'; + +export default function ChangePasswordForm() { + const router = useRouter(); + const [loading, setLoading] = useState(false); + const formData = useRef({ password: '' }); + + const onSubmit = useCallback(async (e<%=#isTypeScript%>: React.FormEvent<%=/isTypeScript%>) => { + e.preventDefault(); + const { password } = formData.current; + setLoading(true); + + const result = await changePassword(password); + setLoading(false); + + if (result.isOk) { + router.push('/login'); + } else { + notify(result.message, 'error', 2000); + } +}, [router]); + +const confirmPassword = useCallback( + ({ value }<%=#isTypeScript%>: ValidationCallbackData<%=/isTypeScript%>) => value === formData.current.password, + [] +); + +return ( +
+ + + + + + + + + + + + { + loading + ? + : 'Continue' + } + + + +
+ +); +} + +const passwordEditorOptions = { stylingMode: 'filled', placeholder: 'Password', mode: 'password' }; +const confirmedPasswordEditorOptions = { stylingMode: 'filled', placeholder: 'Confirm Password', mode: 'password' }; diff --git a/packages/devextreme-cli/src/templates/nextjs/application/src/components/create-account-form/CreateAccountForm.scss b/packages/devextreme-cli/src/templates/nextjs/application/src/components/create-account-form/CreateAccountForm.scss new file mode 100644 index 000000000..830ab7065 --- /dev/null +++ b/packages/devextreme-cli/src/templates/nextjs/application/src/components/create-account-form/CreateAccountForm.scss @@ -0,0 +1,19 @@ +.create-account-form { + .policy-info { + color: var(--base-text-color-alpha-7); + font-size: 12px; + font-style: normal; + + a { + color: var(--base-text-color-alpha-7); + } + } + + .login-link { + color: var(--base-accent); + font-size: 12px; + text-align: center; + padding: 6px 0 32px 0; + border-bottom: 1px solid var(--border-color); + } +} diff --git a/packages/devextreme-cli/src/templates/nextjs/application/src/components/create-account-form/CreateAccountForm.tsx b/packages/devextreme-cli/src/templates/nextjs/application/src/components/create-account-form/CreateAccountForm.tsx new file mode 100644 index 000000000..1d7c8fe4f --- /dev/null +++ b/packages/devextreme-cli/src/templates/nextjs/application/src/components/create-account-form/CreateAccountForm.tsx @@ -0,0 +1,107 @@ +'use client' +import <%=#isTypeScript%>React, <%=/isTypeScript%>{ useState, useRef, useCallback } from 'react'; +import { useRouter } from 'next/navigation'; +import Link from 'next/link'; +import Form, { + Item, + Label, + ButtonItem, + ButtonOptions, + RequiredRule, + CustomRule, + EmailRule +} from 'devextreme-react/form'; +import notify from 'devextreme/ui/notify'; +import LoadIndicator from 'devextreme-react/load-indicator'; +import { signUp } from '@/app/actions/auth'; +<%=#isTypeScript%>import { ValidationCallbackData } from 'devextreme-react/common';<%=/isTypeScript%> +import './CreateAccountForm.scss'; + +export default function CreateAccountForm() { + const router = useRouter(); + const [loading, setLoading] = useState(false); + const formData = useRef({ email: '', password: '' }); + + const onSubmit = useCallback(async (e<%=#isTypeScript%>: React.FormEvent<%=/isTypeScript%>) => { + e.preventDefault(); + const { email, password } = formData.current; + setLoading(true); + + const result = await signUp(email, password); + setLoading(false); + + if (result.isOk) { + router.push('/login'); + } else { + notify(result.message, 'error', 2000); + } + }, [router]); + + const confirmPassword = useCallback( + ({ value }<%=#isTypeScript%>: ValidationCallbackData<%=/isTypeScript%>) => value === formData.current.password, + [] + ); + + return ( +
+ + + + + + + + + + + + + +
+ By creating an account, you agree to the Terms of Service and Privacy Policy +
+
+ + + + { + loading + ? + : 'Create a new account' + } + + + +
+
+ Have an account? Sign In +
+ + ); +} + +const emailEditorOptions = { stylingMode: 'filled', placeholder: 'Email', mode: 'email' }; +const passwordEditorOptions = { stylingMode: 'filled', placeholder: 'Password', mode: 'password' }; +const confirmedPasswordEditorOptions = { stylingMode: 'filled', placeholder: 'Confirm Password', mode: 'password' }; diff --git a/packages/devextreme-cli/src/templates/nextjs/application/src/components/footer/Footer.scss b/packages/devextreme-cli/src/templates/nextjs/application/src/components/footer/Footer.scss new file mode 100644 index 000000000..c1dc94dea --- /dev/null +++ b/packages/devextreme-cli/src/templates/nextjs/application/src/components/footer/Footer.scss @@ -0,0 +1,12 @@ +.footer { + display: block; + color: var(--base-text-color-alpha-7); + border-top: 1px solid var(--footer-border-color); + padding-top: 20px; + padding-bottom: 24px; + margin: 0 40px; + + @media (max-width: 599.99px) { + margin: 0 20px; + } +} diff --git a/packages/devextreme-cli/src/templates/nextjs/application/src/components/footer/Footer.tsx b/packages/devextreme-cli/src/templates/nextjs/application/src/components/footer/Footer.tsx new file mode 100644 index 000000000..bc82bfbd5 --- /dev/null +++ b/packages/devextreme-cli/src/templates/nextjs/application/src/components/footer/Footer.tsx @@ -0,0 +1,5 @@ +import './Footer.scss'; + +export default function Footer({ ...rest }) { + return