mage-obsidian 1.1.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +38 -0
- package/.github/workflows/publish.yml +26 -25
- package/.oxlintrc.json +19 -0
- package/.prettierignore +7 -0
- package/.prettierrc.json +7 -0
- package/LICENSE +1 -1
- package/README.md +1 -0
- package/eslint.config.js +24 -0
- package/package.json +39 -13
- package/pnpm-workspace.yaml +4 -0
- package/src/cli/buildThemes.ts +263 -0
- package/src/config/default.ts +31 -0
- package/src/config/eslint.js +60 -0
- package/src/core/configResolver.ts +141 -0
- package/src/core/contractValidator.ts +56 -0
- package/src/core/cssResolver.ts +153 -0
- package/src/{service/generateInterceptors.js → core/generateInterceptors.ts} +79 -46
- package/src/core/generateJsconfig.ts +168 -0
- package/src/{service/moduleResolver.js → core/moduleResolver.ts} +79 -46
- package/src/core/preCompileFiles.ts +105 -0
- package/src/core/preCompileMagentoFiles.ts +34 -0
- package/src/core/themeResolverSync.ts +66 -0
- package/src/runtime/i18nCore.ts +113 -0
- package/src/runtime/interceptorManager.ts +186 -0
- package/src/runtime/islands.ts +84 -0
- package/src/runtime/sectionStoreCore.ts +259 -0
- package/src/utils/{findComponents.js → findComponents.ts} +29 -16
- package/src/utils/runWithConcurrency.ts +35 -0
- package/src/vite/defaultNodeResolver.ts +17 -0
- package/src/{service/inheritAssetsModuleResolver.js → vite/inheritAssetsModuleResolver.ts} +15 -11
- package/src/{service/inheritModuleResolver.js → vite/inheritModuleResolver.ts} +6 -10
- package/src/{service/interceptorsPlugin.js → vite/interceptorsPlugin.ts} +25 -19
- package/src/vite/magentoHrmRewrite.ts +44 -0
- package/src/vite/sharedPlugins.ts +54 -0
- package/src/vite/themeSourceWatcher.ts +96 -0
- package/src/vite/unresolvedModuleGuard.ts +87 -0
- package/tsconfig.json +34 -0
- package/vitest.config.js +18 -0
- package/jest.config.js +0 -5
- package/src/__tests__/__mocks__/configResolver.js +0 -35
- package/src/__tests__/fixtures/interceptors/emptyTarget.js +0 -2
- package/src/__tests__/fixtures/interceptors/pluginForNonFunction.js +0 -7
- package/src/__tests__/fixtures/interceptors/pluginModule.js +0 -13
- package/src/__tests__/fixtures/interceptors/targetModule.js +0 -12
- package/src/__tests__/fixtures/interceptors/targetNonFunction.js +0 -7
- package/src/__tests__/magento_scenarios/app/code/Vendor/ModuleNameA/view/frontend/web/components/ComponentA.vue +0 -0
- package/src/__tests__/magento_scenarios/app/code/Vendor/ModuleNameA/view/frontend/web/components/ComponentB.vue +0 -0
- package/src/__tests__/magento_scenarios/app/code/Vendor/ModuleNameA/view/frontend/web/css/module.extend.css +0 -0
- package/src/__tests__/magento_scenarios/app/code/Vendor/ModuleNameA/view/frontend/web/js/main.js +0 -0
- package/src/__tests__/magento_scenarios/app/code/Vendor/ModuleNameA/view/frontend/web/main.js +0 -0
- package/src/__tests__/magento_scenarios/app/code/Vendor/ModuleNameA/view/frontend/web/module.config.js +0 -9
- package/src/__tests__/magento_scenarios/app/code/Vendor/ModuleNameB/view/frontend/web/components/ComponentA.vue +0 -0
- package/src/__tests__/magento_scenarios/app/code/Vendor/ModuleNameB/view/frontend/web/components/ComponentB.vue +0 -0
- package/src/__tests__/magento_scenarios/app/code/Vendor/ModuleNameB/view/frontend/web/css/module.extend.css +0 -2
- package/src/__tests__/magento_scenarios/app/code/Vendor/ModuleNameB/view/frontend/web/js/main.js +0 -0
- package/src/__tests__/magento_scenarios/app/code/Vendor/ModuleNameB/view/frontend/web/module.config.js +0 -7
- package/src/__tests__/magento_scenarios/app/code/Vendor/ModuleNameC/view/frontend/web/components/ComponentA.vue +0 -0
- package/src/__tests__/magento_scenarios/app/code/Vendor/ModuleNameC/view/frontend/web/components/ComponentB.vue +0 -0
- package/src/__tests__/magento_scenarios/app/code/Vendor/ModuleNameC/view/frontend/web/css/module.extend.css +0 -2
- package/src/__tests__/magento_scenarios/app/code/Vendor/ModuleNameC/view/frontend/web/js/main.js +0 -0
- package/src/__tests__/magento_scenarios/app/code/Vendor/ModuleNameC/view/frontend/web/module.config.js +0 -7
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameA/web/components/ComponentA.vue +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameA/web/components/ComponentB.vue +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameA/web/css/module.extend.css +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameA/web/js/main.js +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameA/web/module.config.js +0 -2
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameB/web/components/ComponentA.vue +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameB/web/components/ComponentB.vue +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameB/web/css/module.extend.css +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameB/web/js/main.js +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameB/web/module.config.js +0 -2
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameNoConfig/web/components/ComponentA.vue +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameNoConfig/web/components/ComponentB.vue +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameNoConfig/web/css/module.extend.css +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameNoConfig/web/js/main.js +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/Vendor_ModuleNameNoConfig/web/module.config.js +0 -2
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/web/components/ComponentA.vue +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/web/components/ComponentB.vue +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/web/css/theme.source.css +0 -6
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/web/js/main.js +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-a/web/theme.config.js +0 -5
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-b/web/css/theme.source.css +0 -6
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-b/web/theme.config.js +0 -5
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-c/Vendor_ModuleNameB/web/components/ComponentA.vue +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-c/Vendor_ModuleNameB/web/components/ComponentB.vue +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-c/Vendor_ModuleNameB/web/css/module.extend.css +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-c/Vendor_ModuleNameB/web/js/main.js +0 -0
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-c/Vendor_ModuleNameB/web/module.config.js +0 -2
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-c/web/css/theme.source.css +0 -6
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-c/web/theme.config.js +0 -2
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-d/web/css/theme.source.css +0 -6
- package/src/__tests__/magento_scenarios/app/design/frontend/Vendor/theme-d/web/theme.config.js +0 -3
- package/src/__tests__/magento_scenarios/app/etc/mage_obsidian_frontend_modules_a.json +0 -53
- package/src/__tests__/magento_scenarios/app/etc/mage_obsidian_frontend_modules_b.json +0 -50
- package/src/__tests__/service/generateInterceptors.test.js +0 -196
- package/src/__tests__/service/inheritModuleResolver.test.js +0 -61
- package/src/__tests__/service/interceptorManager_magento_behavior.test.js +0 -137
- package/src/__tests__/service/moduleResolver.test.js +0 -194
- package/src/__tests__/service/themeResolverSync.test.js +0 -97
- package/src/__tests__/vite-plugins/interceptorsPlugin.test.js +0 -121
- package/src/config/default.js +0 -27
- package/src/scripts/buildThemes.js +0 -132
- package/src/service/configResolver.js +0 -63
- package/src/service/cssResolver.js +0 -73
- package/src/service/defaultNodeResolver.js +0 -16
- package/src/service/interceptorManager.js +0 -137
- package/src/service/magentoHrmRewrite.js +0 -43
- package/src/service/preCompileFiles.js +0 -37
- package/src/service/preCompileMagentoFiles.js +0 -16
- package/src/service/setupGlobals.js +0 -19
- package/src/service/themeResolverSync.js +0 -67
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
unit:
|
|
9
|
+
name: Unit tests (Node ${{ matrix.node }})
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
node: ["20", "22"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-node@v4
|
|
17
|
+
with:
|
|
18
|
+
node-version: ${{ matrix.node }}
|
|
19
|
+
- run: npm install
|
|
20
|
+
- run: npm test
|
|
21
|
+
|
|
22
|
+
lint:
|
|
23
|
+
name: Lint & format
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- uses: actions/setup-node@v4
|
|
28
|
+
with:
|
|
29
|
+
node-version: "22"
|
|
30
|
+
- run: npm install
|
|
31
|
+
- name: Typecheck
|
|
32
|
+
run: npm run typecheck
|
|
33
|
+
- name: Oxlint (fast pre-filter)
|
|
34
|
+
run: npm run lint:fast
|
|
35
|
+
- name: ESLint
|
|
36
|
+
run: npm run lint
|
|
37
|
+
- name: Prettier format check
|
|
38
|
+
run: npm run format:check
|
|
@@ -1,35 +1,36 @@
|
|
|
1
1
|
name: Publish to npm
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- master
|
|
7
7
|
|
|
8
8
|
jobs:
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
id-token: write
|
|
13
|
+
contents: read
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout code
|
|
17
|
+
uses: actions/checkout@v4
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
- uses: pnpm/action-setup@v4
|
|
20
|
+
name: Install pnpm
|
|
21
|
+
with:
|
|
22
|
+
version: 11
|
|
23
|
+
run_install: false
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
- name: Install Node.js
|
|
26
|
+
uses: actions/setup-node@v4
|
|
27
|
+
with:
|
|
28
|
+
node-version: 22
|
|
29
|
+
cache: "pnpm"
|
|
30
|
+
registry-url: "https://registry.npmjs.org"
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: pnpm install
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
35
|
-
run: pnpm publish --no-git-checks
|
|
35
|
+
- name: Publish to npm
|
|
36
|
+
run: pnpm publish --no-git-checks
|
package/.oxlintrc.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
|
3
|
+
"ignorePatterns": [
|
|
4
|
+
"node_modules",
|
|
5
|
+
".precompiled",
|
|
6
|
+
"src/__tests__/magento_scenarios",
|
|
7
|
+
"src/__tests__/fixtures",
|
|
8
|
+
"src/__tests__/__mocks__",
|
|
9
|
+
"src/templates"
|
|
10
|
+
],
|
|
11
|
+
"categories": {
|
|
12
|
+
"correctness": "error"
|
|
13
|
+
},
|
|
14
|
+
"env": {
|
|
15
|
+
"node": true,
|
|
16
|
+
"browser": true,
|
|
17
|
+
"es2024": true
|
|
18
|
+
}
|
|
19
|
+
}
|
package/.prettierignore
ADDED
package/.prettierrc.json
ADDED
package/LICENSE
CHANGED
package/README.md
CHANGED
package/eslint.config.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import globals from "globals";
|
|
2
|
+
import preset from "./src/config/eslint.js";
|
|
3
|
+
|
|
4
|
+
// The build engine itself: ESM, Node runtime. Data trees under __tests__ and
|
|
5
|
+
// generated/template files are not linted.
|
|
6
|
+
export default [
|
|
7
|
+
{
|
|
8
|
+
ignores: [
|
|
9
|
+
"node_modules/**",
|
|
10
|
+
".precompiled/**",
|
|
11
|
+
"src/__tests__/magento_scenarios/**",
|
|
12
|
+
"src/__tests__/fixtures/**",
|
|
13
|
+
"src/__tests__/__mocks__/**",
|
|
14
|
+
"src/templates/**",
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
...preset,
|
|
18
|
+
{
|
|
19
|
+
files: ["**/*.test.{js,ts}", "src/__tests__/**"],
|
|
20
|
+
languageOptions: {
|
|
21
|
+
globals: { ...globals.vitest },
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
];
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mage-obsidian",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Mage Obsidian Ultis",
|
|
5
|
-
"
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/mage-obsidian/js-package-utils.git"
|
|
8
|
+
},
|
|
9
|
+
"main": "./src/cli/buildThemes.ts",
|
|
6
10
|
"keywords": [
|
|
7
11
|
"Magento",
|
|
8
12
|
"MageObsidian"
|
|
@@ -11,31 +15,53 @@
|
|
|
11
15
|
"license": "MIT",
|
|
12
16
|
"type": "module",
|
|
13
17
|
"bin": {
|
|
14
|
-
"mage-obsidian:build-themes": "./src/
|
|
18
|
+
"mage-obsidian:build-themes": "./src/cli/buildThemes.ts"
|
|
15
19
|
},
|
|
16
20
|
"imports": {
|
|
21
|
+
"#core/*": "./src/core/*",
|
|
22
|
+
"#vite/*": "./src/vite/*",
|
|
23
|
+
"#runtime/*": "./src/runtime/*",
|
|
24
|
+
"#cli/*": "./src/cli/*",
|
|
17
25
|
"#utils/*": "./src/utils/*",
|
|
18
|
-
"#config/*": "./src/config/*"
|
|
19
|
-
"#service/*": "./src/service/*"
|
|
26
|
+
"#config/*": "./src/config/*"
|
|
20
27
|
},
|
|
21
28
|
"exports": {
|
|
22
|
-
".": "./src/
|
|
23
|
-
"./
|
|
24
|
-
"./
|
|
25
|
-
"./
|
|
26
|
-
"./
|
|
29
|
+
".": "./src/cli/buildThemes.ts",
|
|
30
|
+
"./core/*": "./src/core/*",
|
|
31
|
+
"./vite/*": "./src/vite/*",
|
|
32
|
+
"./runtime/*": "./src/runtime/*",
|
|
33
|
+
"./cli/*": "./src/cli/*",
|
|
34
|
+
"./utils/*": "./src/utils/*",
|
|
35
|
+
"./config/*": "./src/config/*"
|
|
27
36
|
},
|
|
28
37
|
"dependencies": {
|
|
29
38
|
"chalk": "^5.4.1",
|
|
30
39
|
"commander": "^13.1.0",
|
|
31
40
|
"deepmerge": "^4.3.1",
|
|
41
|
+
"dotenv": "^16.6.1",
|
|
32
42
|
"readline-sync": "^1.4.10"
|
|
33
43
|
},
|
|
34
44
|
"devDependencies": {
|
|
35
|
-
"
|
|
36
|
-
"
|
|
45
|
+
"@eslint/js": "^10.0.1",
|
|
46
|
+
"@types/node": "^22.19.21",
|
|
47
|
+
"eslint": "^10.5.0",
|
|
48
|
+
"eslint-config-prettier": "^10.1.8",
|
|
49
|
+
"eslint-plugin-vue": "^10.9.2",
|
|
50
|
+
"globals": "^17.6.0",
|
|
51
|
+
"oxlint": "^1.70.0",
|
|
52
|
+
"prettier": "^3.8.4",
|
|
53
|
+
"typescript": "^5.9.3",
|
|
54
|
+
"typescript-eslint": "^8.61.1",
|
|
55
|
+
"vitest": "^3.2.6"
|
|
37
56
|
},
|
|
38
57
|
"scripts": {
|
|
39
|
-
"test": "
|
|
58
|
+
"test": "vitest run",
|
|
59
|
+
"test:watch": "vitest",
|
|
60
|
+
"typecheck": "tsc --noEmit",
|
|
61
|
+
"lint": "eslint .",
|
|
62
|
+
"lint:fix": "eslint . --fix",
|
|
63
|
+
"lint:fast": "oxlint",
|
|
64
|
+
"format": "prettier --write .",
|
|
65
|
+
"format:check": "prettier --check ."
|
|
40
66
|
}
|
|
41
67
|
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#! /usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { execSync, spawn } from "child_process";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import readlineSync from "readline-sync";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import configResolver from "../core/configResolver.ts";
|
|
10
|
+
import preCompileMagentoFiles from "../core/preCompileMagentoFiles.ts";
|
|
11
|
+
import runWithConcurrency from "../utils/runWithConcurrency.ts";
|
|
12
|
+
import dotenv from "dotenv";
|
|
13
|
+
dotenv.config();
|
|
14
|
+
|
|
15
|
+
const program = new Command();
|
|
16
|
+
|
|
17
|
+
program
|
|
18
|
+
.option("--theme <theme>", "Specify the active theme")
|
|
19
|
+
.option("--dev-server", "Run the development server for a specific theme")
|
|
20
|
+
.option("--typecheck", "Type-check theme sources with vue-tsc instead of building");
|
|
21
|
+
|
|
22
|
+
program.parse(process.argv);
|
|
23
|
+
|
|
24
|
+
const options = program.opts();
|
|
25
|
+
const themesConfig = configResolver.getMagentoConfig().themes;
|
|
26
|
+
|
|
27
|
+
if (options.devServer && !options.theme) {
|
|
28
|
+
console.error(chalk.red("Error: The --theme option is required when using --dev-server."));
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const theme = options.theme || "all";
|
|
33
|
+
|
|
34
|
+
const executeCommand = (command) => {
|
|
35
|
+
try {
|
|
36
|
+
execSync(command, { stdio: "inherit" });
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error(chalk.red(`Command failed: ${command}`), error);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
function tryCreateEnvFile() {
|
|
44
|
+
const defaultEnvVars = {
|
|
45
|
+
VITE_SERVER_HOST: "phpfpm",
|
|
46
|
+
VITE_SERVER_PORT: "5173",
|
|
47
|
+
VITE_SERVER_SECURE: "true",
|
|
48
|
+
VITE_HMR_PATH: "/__vite_ping",
|
|
49
|
+
MAGENTO_HOST: "magento.test",
|
|
50
|
+
VITE_SERVER_ALLOWED_HOSTS: "magento.test,localhost",
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
console.log(chalk.blue("Creating `.env` file with default or user-provided values..."));
|
|
54
|
+
|
|
55
|
+
const envFilePath = path.resolve(process.cwd(), ".env");
|
|
56
|
+
if (fs.existsSync(envFilePath)) {
|
|
57
|
+
console.log(chalk.green(".env file already exists."));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let envContent = "";
|
|
62
|
+
for (const [key, defaultValue] of Object.entries(defaultEnvVars)) {
|
|
63
|
+
const userInput = readlineSync.question(
|
|
64
|
+
chalk.yellow(`Enter value for ${key} (default: ${defaultValue}): `),
|
|
65
|
+
{
|
|
66
|
+
defaultInput: defaultValue,
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
envContent += `${key}=${userInput || defaultValue}\n`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fs.writeFileSync(envFilePath, envContent);
|
|
73
|
+
console.log(chalk.green(".env file created successfully."));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function validateEnv() {
|
|
77
|
+
const requiredEnvVars = [
|
|
78
|
+
"VITE_SERVER_HOST",
|
|
79
|
+
"VITE_SERVER_PORT",
|
|
80
|
+
"VITE_SERVER_SECURE",
|
|
81
|
+
"VITE_HMR_PATH",
|
|
82
|
+
"MAGENTO_HOST",
|
|
83
|
+
"VITE_SERVER_ALLOWED_HOSTS",
|
|
84
|
+
];
|
|
85
|
+
const missingEnvVars = [];
|
|
86
|
+
for (const envVar of requiredEnvVars) {
|
|
87
|
+
if (!process.env[envVar]) {
|
|
88
|
+
missingEnvVars.push(envVar);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (missingEnvVars.length > 0) {
|
|
92
|
+
console.error(
|
|
93
|
+
chalk.red(`Missing required environment variables: ${missingEnvVars.join(", ")}`),
|
|
94
|
+
);
|
|
95
|
+
tryCreateEnvFile();
|
|
96
|
+
console.log(chalk.green("Please restart the script after setting up the .env file."));
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Build a single theme in its own child process. Each child gets its own
|
|
103
|
+
* CURRENT_THEME env so several themes can build concurrently without sharing
|
|
104
|
+
* in-process state. Output is buffered and printed as a labeled block on exit
|
|
105
|
+
* to keep parallel logs readable. Never rejects — failures surface via the
|
|
106
|
+
* returned exit code so sibling builds aren't aborted.
|
|
107
|
+
*
|
|
108
|
+
* @param {string} themeName
|
|
109
|
+
* @returns {Promise<{ themeName: string, code: number }>}
|
|
110
|
+
*/
|
|
111
|
+
const spawnBuild = (themeName) =>
|
|
112
|
+
new Promise<{ themeName: string; code: number }>((resolve) => {
|
|
113
|
+
const child = spawn("vite build", {
|
|
114
|
+
shell: true,
|
|
115
|
+
env: { ...process.env, CURRENT_THEME: themeName },
|
|
116
|
+
});
|
|
117
|
+
const chunks = [];
|
|
118
|
+
child.stdout.on("data", (data) => chunks.push(data));
|
|
119
|
+
child.stderr.on("data", (data) => chunks.push(data));
|
|
120
|
+
const finish = (code) => {
|
|
121
|
+
console.log(chalk.cyan(`\n=== ${themeName} ===`));
|
|
122
|
+
process.stdout.write(Buffer.concat(chunks).toString());
|
|
123
|
+
if (code === 0) {
|
|
124
|
+
console.log(chalk.green(`✓ ${themeName} built`));
|
|
125
|
+
} else {
|
|
126
|
+
console.error(chalk.red(`✗ ${themeName} failed (exit ${code})`));
|
|
127
|
+
}
|
|
128
|
+
resolve({ themeName, code });
|
|
129
|
+
};
|
|
130
|
+
child.on("close", finish);
|
|
131
|
+
child.on("error", (error) => {
|
|
132
|
+
chunks.push(Buffer.from(String(error?.stack ?? error)));
|
|
133
|
+
finish(1);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const buildThemes = async (themeNames) => {
|
|
138
|
+
const cores = os.cpus()?.length ?? 2;
|
|
139
|
+
const limit = Math.max(1, Math.min(themeNames.length, cores - 1));
|
|
140
|
+
console.log(
|
|
141
|
+
chalk.cyan(
|
|
142
|
+
`Building ${themeNames.length} theme(s) (concurrency ${limit}): ${themeNames.join(", ")}`,
|
|
143
|
+
),
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const results = await runWithConcurrency(
|
|
147
|
+
themeNames,
|
|
148
|
+
(themeName) => spawnBuild(themeName),
|
|
149
|
+
limit,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
const failed = results.filter((result) => result.code !== 0);
|
|
153
|
+
if (failed.length > 0) {
|
|
154
|
+
console.error(
|
|
155
|
+
chalk.red(`Build failed for: ${failed.map((result) => result.themeName).join(", ")}`),
|
|
156
|
+
);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Type-check one assembled theme with vue-tsc. The theme's generated
|
|
163
|
+
* tsconfig.typecheck.json (materialized by precompile) carries the full
|
|
164
|
+
* `Vendor_Module::` paths, so every cross-module seam resolves. vue-tsc (not
|
|
165
|
+
* plain tsc) is required to check inside `<script setup lang="ts">` SFCs. Output
|
|
166
|
+
* is buffered and printed as a labeled block; never rejects — failures surface
|
|
167
|
+
* via the exit code so sibling themes still run.
|
|
168
|
+
*
|
|
169
|
+
* @param {string} themeName
|
|
170
|
+
* @returns {Promise<{ themeName: string, code: number }>}
|
|
171
|
+
*/
|
|
172
|
+
const spawnTypecheck = (themeName) =>
|
|
173
|
+
new Promise<{ themeName: string; code: number }>((resolve) => {
|
|
174
|
+
const themeDefinition = configResolver.getThemeDefinition(themeName);
|
|
175
|
+
const tsconfigPath = path.join(themeDefinition.src, "tsconfig.typecheck.json");
|
|
176
|
+
const child = spawn(`vue-tsc --noEmit -p "${tsconfigPath}"`, {
|
|
177
|
+
shell: true,
|
|
178
|
+
env: { ...process.env, CURRENT_THEME: themeName },
|
|
179
|
+
});
|
|
180
|
+
const chunks = [];
|
|
181
|
+
child.stdout.on("data", (data) => chunks.push(data));
|
|
182
|
+
child.stderr.on("data", (data) => chunks.push(data));
|
|
183
|
+
const finish = (code) => {
|
|
184
|
+
console.log(chalk.cyan(`\n=== ${themeName} (typecheck) ===`));
|
|
185
|
+
process.stdout.write(Buffer.concat(chunks).toString());
|
|
186
|
+
if (code === 0) {
|
|
187
|
+
console.log(chalk.green(`✓ ${themeName} type-checks`));
|
|
188
|
+
} else {
|
|
189
|
+
console.error(chalk.red(`✗ ${themeName} has type errors (exit ${code})`));
|
|
190
|
+
}
|
|
191
|
+
resolve({ themeName, code });
|
|
192
|
+
};
|
|
193
|
+
child.on("close", finish);
|
|
194
|
+
child.on("error", (error) => {
|
|
195
|
+
chunks.push(Buffer.from(String(error?.stack ?? error)));
|
|
196
|
+
finish(1);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const typecheckThemes = async (themeNames) => {
|
|
201
|
+
// Materialize each theme's tsconfig.typecheck.json + inheritance map in-process
|
|
202
|
+
// (idempotent, cached by contract hash) before spawning vue-tsc against it.
|
|
203
|
+
for (const themeName of themeNames) {
|
|
204
|
+
await preCompileMagentoFiles(themeName);
|
|
205
|
+
}
|
|
206
|
+
console.log(
|
|
207
|
+
chalk.cyan(`Type-checking ${themeNames.length} theme(s): ${themeNames.join(", ")}`),
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const results = await runWithConcurrency(
|
|
211
|
+
themeNames,
|
|
212
|
+
(themeName) => spawnTypecheck(themeName),
|
|
213
|
+
1,
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const failed = results.filter((result) => result.code !== 0);
|
|
217
|
+
if (failed.length > 0) {
|
|
218
|
+
console.error(
|
|
219
|
+
chalk.red(`Type check failed for: ${failed.map((r) => r.themeName).join(", ")}`),
|
|
220
|
+
);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const runDevServer = (themeName) => {
|
|
226
|
+
console.log(chalk.cyan(`Starting development server for theme: ${themeName}`));
|
|
227
|
+
try {
|
|
228
|
+
const theme = themesConfig[themeName];
|
|
229
|
+
if (!theme) {
|
|
230
|
+
console.error(chalk.red(`Theme "${themeName}" does not exist.`));
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
process.env.CURRENT_THEME = themeName;
|
|
235
|
+
executeCommand("vite");
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.error(
|
|
238
|
+
chalk.red(`Failed to start development server for theme "${themeName}":`),
|
|
239
|
+
error,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
validateEnv();
|
|
245
|
+
|
|
246
|
+
if (options.devServer) {
|
|
247
|
+
runDevServer(theme);
|
|
248
|
+
} else {
|
|
249
|
+
let themeNames;
|
|
250
|
+
if (theme === "all") {
|
|
251
|
+
themeNames = Object.keys(themesConfig);
|
|
252
|
+
} else if (themesConfig[theme]) {
|
|
253
|
+
themeNames = [theme];
|
|
254
|
+
} else {
|
|
255
|
+
console.error(chalk.red(`Theme "${theme}" does not exist.`));
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
if (options.typecheck) {
|
|
259
|
+
await typecheckThemes(themeNames);
|
|
260
|
+
} else {
|
|
261
|
+
await buildThemes(themeNames);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
// The Vite harness runs from the `vite/` dir one level below the Magento root.
|
|
4
|
+
// Allow an explicit root override so the engine is not tied to that cwd layout
|
|
5
|
+
// (e.g. when invoked from elsewhere or in tests).
|
|
6
|
+
export const MAGENTO_ROOT = process.env.MAGE_OBSIDIAN_MAGENTO_ROOT
|
|
7
|
+
? path.resolve(process.env.MAGE_OBSIDIAN_MAGENTO_ROOT)
|
|
8
|
+
: path.resolve(process.cwd(), "..");
|
|
9
|
+
|
|
10
|
+
export const DEPENDENCY_CONFIG_FILENAME = "mage_obsidian_frontend_modules.json";
|
|
11
|
+
export const MAGENTO_CONFIG_RELATIVE_PATH = "app/etc/";
|
|
12
|
+
export const DEPENDENCY_CONFIG_FILE_PATH = path.resolve(
|
|
13
|
+
MAGENTO_ROOT,
|
|
14
|
+
MAGENTO_CONFIG_RELATIVE_PATH,
|
|
15
|
+
DEPENDENCY_CONFIG_FILENAME,
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
export const MODULE_WEB_PATH = "view/frontend/web/";
|
|
19
|
+
export const MODULE_TEMPLATES_PATH = "view/frontend/templates";
|
|
20
|
+
export const THEME_MODULE_WEB_PATH = "web/";
|
|
21
|
+
export const MODULE_CSS_FOLDER = "css";
|
|
22
|
+
|
|
23
|
+
export const THEME_CSS_FOLDER = "css";
|
|
24
|
+
|
|
25
|
+
export const OUTPUT_THEME_DIR = "web/generated";
|
|
26
|
+
export const OUTPUT_JS_DIR = "js/";
|
|
27
|
+
export const OUTPUT_CSS_DIR = "css/";
|
|
28
|
+
export const PRECOMPILED_FOLDER = ".precompiled";
|
|
29
|
+
export const INTERCEPTORS_FOLDER = "interceptors";
|
|
30
|
+
|
|
31
|
+
export const ALL_JS_VUE_FILES_WITH_INHERITANCE_FILE_NAME = "allJsVueFilesWithInheritance.json";
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import js from "@eslint/js";
|
|
2
|
+
import globals from "globals";
|
|
3
|
+
import pluginVue from "eslint-plugin-vue";
|
|
4
|
+
import tseslint from "typescript-eslint";
|
|
5
|
+
import prettier from "eslint-config-prettier/flat";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Shared flat ESLint preset for the MageObsidian stack. Consumers spread the
|
|
9
|
+
* default export and add their own `files`/`ignores`. Every plugin is imported
|
|
10
|
+
* here (inside the mage-obsidian package), so a consuming repo only needs
|
|
11
|
+
* `eslint` itself installed — the plugins resolve relative to this file.
|
|
12
|
+
*
|
|
13
|
+
* JS today, TypeScript after the engine migration: the TS rules are scoped to
|
|
14
|
+
* `*.ts*` and the Vue `<script>` parser is already the TS parser, so `.vue`
|
|
15
|
+
* files lint whether their script block is JS or TS.
|
|
16
|
+
*/
|
|
17
|
+
const unusedVarsOptions = {
|
|
18
|
+
argsIgnorePattern: "^_",
|
|
19
|
+
varsIgnorePattern: "^_",
|
|
20
|
+
caughtErrors: "none",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default tseslint.config(
|
|
24
|
+
js.configs.recommended,
|
|
25
|
+
{
|
|
26
|
+
languageOptions: {
|
|
27
|
+
ecmaVersion: "latest",
|
|
28
|
+
sourceType: "module",
|
|
29
|
+
globals: { ...globals.node, ...globals.browser },
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
// Plain JS (author code, engine bits not yet migrated): the base rule.
|
|
34
|
+
files: ["**/*.{js,mjs,cjs}"],
|
|
35
|
+
rules: {
|
|
36
|
+
"no-unused-vars": ["error", unusedVarsOptions],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
files: ["**/*.{ts,mts,cts,tsx}"],
|
|
41
|
+
extends: [tseslint.configs.recommended],
|
|
42
|
+
rules: {
|
|
43
|
+
// tseslint disables the base rule for TS; use the TS-aware one so
|
|
44
|
+
// labels in function-type annotations aren't flagged as unused.
|
|
45
|
+
"@typescript-eslint/no-unused-vars": ["error", unusedVarsOptions],
|
|
46
|
+
// Lenient migration baseline: the engine is typed progressively, so
|
|
47
|
+
// explicit `any` is allowed but surfaced as a warning (ratchet
|
|
48
|
+
// target, alongside tsconfig `strict`).
|
|
49
|
+
"@typescript-eslint/no-explicit-any": "warn",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
files: ["**/*.vue"],
|
|
54
|
+
extends: [pluginVue.configs["flat/recommended"]],
|
|
55
|
+
languageOptions: {
|
|
56
|
+
parserOptions: { parser: tseslint.parser },
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
prettier,
|
|
60
|
+
);
|