create-portfoliable 1.0.3
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/bin/create-portfoliable.mjs +425 -0
- package/cli/portfoliable.mjs +836 -0
- package/index.html +18 -0
- package/package.json +102 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.png +0 -0
- package/scripts/check-updates.mjs +31 -0
- package/scripts/ensure-valence-index-css.mjs +245 -0
- package/scripts/generate-password-hash.mjs +25 -0
- package/scripts/release-orchestrator.mjs +363 -0
- package/scripts/scaffold-case.mjs +460 -0
- package/scripts/scaffold-consumer.mjs +87 -0
- package/scripts/smoke-homeview.mjs +133 -0
- package/scripts/smoke-initializer.mjs +129 -0
- package/scripts/smoke-packed.mjs +184 -0
- package/scripts/switch-valence-source.mjs +162 -0
- package/scripts/sync-template-shared.mjs +136 -0
- package/scripts/terminal-ui.mjs +274 -0
- package/scripts/verify-integration.mjs +72 -0
- package/src/App.js +10015 -0
- package/src/header-visibility.contract.js +31 -0
- package/src/header-visibility.contract.test.js +41 -0
- package/src/main.js +12 -0
- package/src/parser/markdown.js +855 -0
- package/src/style.css +88 -0
- package/templates/README.md +242 -0
- package/templates/configs/i18n/i18n.config.js +230 -0
- package/templates/configs/i18n/i18n.labels.js +188 -0
- package/templates/configs/portfoliable.a11y.config.js +262 -0
- package/templates/configs/portfoliable.design.config.js +651 -0
- package/templates/gitignore +9 -0
- package/templates/index.html +20 -0
- package/templates/public/api/.htaccess +4 -0
- package/templates/public/api/README.md +27 -0
- package/templates/public/api/password.config.example.json +16 -0
- package/templates/public/api/password.config.json +16 -0
- package/templates/public/api/unlock-case.php +56 -0
- package/templates/public/favicon.ico +0 -0
- package/templates/public/favicon.png +0 -0
- package/templates/public/portfoliable-logo.svg +3 -0
- package/templates/scripts/add-language.mjs +253 -0
- package/templates/scripts/convert-video-to-mp4.mjs +207 -0
- package/templates/scripts/convert-wav-to-mp3.mjs +184 -0
- package/templates/scripts/delete-case.mjs +320 -0
- package/templates/scripts/delete-language.mjs +195 -0
- package/templates/scripts/generate-password-hash.mjs +98 -0
- package/templates/scripts/i18n-config-utils.mjs +123 -0
- package/templates/scripts/scaffold-case.mjs +444 -0
- package/templates/scripts/sync-locales.mjs +1048 -0
- package/templates/scripts/validate-content.mjs +257 -0
- package/templates/scripts/validate-protection.mjs +187 -0
- package/templates/src/cases/index.js +188 -0
- package/templates/src/content/about/ABOUTME.md +202 -0
- package/templates/src/content/cases/template-case/case.md +479 -0
- package/templates/src/content/cases/template-case/cover.png +0 -0
- package/templates/src/content/cases/template-case/he-cover.png +0 -0
- package/templates/src/content/cases/template-case/template-audio.mp3 +0 -0
- package/templates/src/content/cases/template-case/template-video.mp4 +0 -0
- package/templates/src/content/thumbnail-options.generated.json +1044 -0
- package/templates/src/main.js +23 -0
- package/templates/src/parser/markdown.js +805 -0
- package/vite.config.js +111 -0
package/index.html
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<!-- File: create-portfoliable/index.html -->
|
|
2
|
+
<!-- Purpose: Define the runtime HTML host document that mounts the Portfoliable app shell. -->
|
|
3
|
+
<!-- Author: Lio Schimanko -->
|
|
4
|
+
|
|
5
|
+
<!doctype html>
|
|
6
|
+
<html lang="en">
|
|
7
|
+
<head>
|
|
8
|
+
<meta charset="UTF-8" />
|
|
9
|
+
<link rel="icon" type="image/png" sizes="32x32" href="/favicon.png?v=1" />
|
|
10
|
+
<link rel="shortcut icon" href="/favicon.ico?v=1" />
|
|
11
|
+
<link rel="apple-touch-icon" href="/favicon.png?v=1" />
|
|
12
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<div id="app"></div>
|
|
16
|
+
<script type="module" src="/src/main.js"></script>
|
|
17
|
+
</body>
|
|
18
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-portfoliable",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Unified Portfoliable package with initializer and runtime CLI",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"create",
|
|
9
|
+
"initializer",
|
|
10
|
+
"portfolio",
|
|
11
|
+
"portfoliable",
|
|
12
|
+
"vite"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/portfoliablejs/portfoliable/tree/main/create-portfoliable",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/portfoliablejs/portfoliable.git",
|
|
18
|
+
"directory": "create-portfoliable"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/portfoliablejs/portfoliable/issues"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"bin",
|
|
25
|
+
"cli",
|
|
26
|
+
"scripts",
|
|
27
|
+
"src",
|
|
28
|
+
"css",
|
|
29
|
+
"public",
|
|
30
|
+
"index.html",
|
|
31
|
+
"vite.config.js",
|
|
32
|
+
"templates",
|
|
33
|
+
"CHANGELOG.md",
|
|
34
|
+
"CODE_OF_CONDUCT.md",
|
|
35
|
+
"CONTRIBUTING.md",
|
|
36
|
+
"SECURITY.md",
|
|
37
|
+
"LICENSE"
|
|
38
|
+
],
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18"
|
|
41
|
+
},
|
|
42
|
+
"main": "src/App.js",
|
|
43
|
+
"bin": {
|
|
44
|
+
"create": "bin/create-portfoliable.mjs",
|
|
45
|
+
"create-portfoliable": "bin/create-portfoliable.mjs",
|
|
46
|
+
"portfoliable": "cli/portfoliable.mjs"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"postinstall": "node ./scripts/ensure-valence-index-css.mjs",
|
|
50
|
+
"ensure:valence": "node ./scripts/ensure-valence-index-css.mjs",
|
|
51
|
+
"validate:content": "node ./cli/portfoliable.mjs validate",
|
|
52
|
+
"validate:protection": "node ./templates/scripts/validate-protection.mjs",
|
|
53
|
+
"convert:audio": "node ./templates/scripts/convert-wav-to-mp3.mjs",
|
|
54
|
+
"convert:video": "node ./templates/scripts/convert-video-to-mp4.mjs",
|
|
55
|
+
"password:hash": "node ./templates/scripts/generate-password-hash.mjs",
|
|
56
|
+
"predev": "npm run ensure:valence && npm run validate:content && npm run validate:protection",
|
|
57
|
+
"prebuild": "npm run ensure:valence && npm run validate:content && npm run validate:protection",
|
|
58
|
+
"prepreview": "npm run ensure:valence && npm run validate:content && npm run validate:protection",
|
|
59
|
+
"portfoliable": "node ./cli/portfoliable.mjs dev",
|
|
60
|
+
"dev": "node ./cli/portfoliable.mjs dev",
|
|
61
|
+
"build": "node ./cli/portfoliable.mjs build",
|
|
62
|
+
"preview": "node ./cli/portfoliable.mjs preview",
|
|
63
|
+
"thumbnail:options": "node ./cli/portfoliable.mjs thumbnail-options",
|
|
64
|
+
"portfoliable-thumbnail-options": "node ./cli/portfoliable.mjs thumbnail-options",
|
|
65
|
+
"portfoliable-thumbnail-option": "node ./cli/portfoliable.mjs thumbnail-options",
|
|
66
|
+
"create:case": "node ./cli/portfoliable.mjs create-case",
|
|
67
|
+
"delete:case": "node ./cli/portfoliable.mjs delete-case",
|
|
68
|
+
"portfoliable-create-case": "node ./cli/portfoliable.mjs create-case",
|
|
69
|
+
"portfoliable-delete-case": "node ./cli/portfoliable.mjs delete-case",
|
|
70
|
+
"sync:locales": "node ./cli/portfoliable.mjs sync-locales",
|
|
71
|
+
"sync:locales:watch": "node ./cli/portfoliable.mjs sync-locales --watch",
|
|
72
|
+
"add:language": "node ./cli/portfoliable.mjs add-language",
|
|
73
|
+
"delete:language": "node ./cli/portfoliable.mjs delete-language",
|
|
74
|
+
"valence:status": "node ./scripts/switch-valence-source.mjs status",
|
|
75
|
+
"valence:local": "node ./scripts/switch-valence-source.mjs local",
|
|
76
|
+
"valence:npm": "node ./scripts/switch-valence-source.mjs npm",
|
|
77
|
+
"verify:integration": "node ./scripts/verify-integration.mjs",
|
|
78
|
+
"smoke:homeview": "npm run validate:protection && node ./scripts/smoke-homeview.mjs",
|
|
79
|
+
"smoke:initializer": "npm run validate:protection && node ./scripts/smoke-initializer.mjs",
|
|
80
|
+
"smoke:packed": "npm run validate:protection && node ./scripts/smoke-packed.mjs",
|
|
81
|
+
"test": "vitest run",
|
|
82
|
+
"test:watch": "vitest"
|
|
83
|
+
},
|
|
84
|
+
"dependencies": {
|
|
85
|
+
"@portfoliablejs/valence": "^1.0.1",
|
|
86
|
+
"lenis": "^1.3.25",
|
|
87
|
+
"markdown-it": "^14.1.0",
|
|
88
|
+
"markdown-it-deflist": "^2.1.0",
|
|
89
|
+
"markdown-it-footnote": "^4.0.0",
|
|
90
|
+
"markdown-it-task-lists": "^2.1.1",
|
|
91
|
+
"vite": "^8.1.1"
|
|
92
|
+
},
|
|
93
|
+
"devDependencies": {
|
|
94
|
+
"vitest": "^4.1.10"
|
|
95
|
+
},
|
|
96
|
+
"license": "MIT",
|
|
97
|
+
"publishConfig": {
|
|
98
|
+
"registry": "https://registry.npmjs.org/",
|
|
99
|
+
"access": "public"
|
|
100
|
+
},
|
|
101
|
+
"funding": "https://github.com/sponsors/schimanko"
|
|
102
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// File: create-portfoliable/scripts/check-updates.mjs
|
|
2
|
+
// Purpose: Check the latest valence repository commit to surface upstream update visibility.
|
|
3
|
+
// Author: Lio Schimanko
|
|
4
|
+
|
|
5
|
+
// MARK: IMPORTS
|
|
6
|
+
import { execSync } from 'node:child_process';
|
|
7
|
+
|
|
8
|
+
void execSync;
|
|
9
|
+
|
|
10
|
+
// MARK: UPDATE CHECK ROUTINE
|
|
11
|
+
// Fetches the latest commit SHA from the valence main branch for quick maintainer awareness.
|
|
12
|
+
async function checkValenceUpdates() {
|
|
13
|
+
try {
|
|
14
|
+
console.log('🔍 Checking @portfoliablejs/valence for updates...');
|
|
15
|
+
// Calls the GitHub commits API to retrieve the current main-branch head.
|
|
16
|
+
const res = await fetch('https://api.github.com/repos/portfoliablejs/valence/commits/main');
|
|
17
|
+
if (!res.ok) return;
|
|
18
|
+
|
|
19
|
+
// Reads the response payload and prints a short SHA for concise terminal output.
|
|
20
|
+
const data = await res.json();
|
|
21
|
+
// Extracts short SHA for concise maintainer output.
|
|
22
|
+
const latestSha = data.sha.substring(0, 7);
|
|
23
|
+
console.log(`✨ Connected to valence commit: ${latestSha}`);
|
|
24
|
+
} catch (e) {
|
|
25
|
+
// Silently continue so offline development works without errors
|
|
26
|
+
void e;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// MARK: SCRIPT ENTRYPOINT
|
|
31
|
+
checkValenceUpdates();
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
// File: create-portfoliable/scripts/ensure-valence-index-css.mjs
|
|
2
|
+
// Purpose: Ensure valence compatibility assets exist locally before runtime build and preview commands.
|
|
3
|
+
// Author: Lio Schimanko
|
|
4
|
+
|
|
5
|
+
// MARK: IMPORTS
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
9
|
+
|
|
10
|
+
// MARK: PATH UTILITIES
|
|
11
|
+
// Deduplicates and normalizes candidate paths used while locating the valence package root.
|
|
12
|
+
function uniquePaths(paths) {
|
|
13
|
+
return [...new Set(paths.map((entry) => path.normalize(entry)))];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Resolves the local filesystem root for @portfoliablejs/valence from common installation layouts.
|
|
17
|
+
function resolveValenceRoot() {
|
|
18
|
+
// Resolves this script directory so candidate paths can be built relative to package root.
|
|
19
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
// Resolves the current package root (create-portfoliable).
|
|
21
|
+
const packageRoot = path.resolve(scriptDir, '..');
|
|
22
|
+
|
|
23
|
+
// Defines installation candidates across consumer and package-local node_modules layouts.
|
|
24
|
+
const candidates = uniquePaths([
|
|
25
|
+
path.resolve(process.cwd(), 'node_modules', '@portfoliablejs', 'valence'),
|
|
26
|
+
path.resolve(packageRoot, 'node_modules', '@portfoliablejs', 'valence'),
|
|
27
|
+
path.resolve(packageRoot, '..', '@portfoliablejs', 'valence')
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
return candidates.find((candidate) => fs.existsSync(candidate) && fs.statSync(candidate).isDirectory()) || null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// MARK: COMPATIBILITY PATCHES
|
|
34
|
+
// Ensures the valence sub-atomic index.css file exists for backward-compatible imports.
|
|
35
|
+
function ensureValenceIndexCss(valenceRoot) {
|
|
36
|
+
// Resolves expected valence CSS entrypoint path.
|
|
37
|
+
const indexCssPath = path.resolve(
|
|
38
|
+
valenceRoot,
|
|
39
|
+
'src',
|
|
40
|
+
'stories',
|
|
41
|
+
'sub-atomic',
|
|
42
|
+
'index.css'
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
if (fs.existsSync(indexCssPath)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
fs.mkdirSync(path.dirname(indexCssPath), { recursive: true });
|
|
50
|
+
fs.writeFileSync(indexCssPath, "@import '../../style.css';\n", 'utf8');
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Ensures consumer mockup assets are linked to valence catalog assets used by thumbnail devices.
|
|
55
|
+
function ensureMockupsSymlink(valenceRoot) {
|
|
56
|
+
// Resolves source mockup directory within valence package.
|
|
57
|
+
const sourceMockups = path.resolve(valenceRoot, 'src', 'stories', 'assets', 'mockups');
|
|
58
|
+
if (!fs.existsSync(sourceMockups) || !fs.statSync(sourceMockups).isDirectory()) {
|
|
59
|
+
return { created: false, reason: 'missing-source' };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Resolves target mockup path in current working tree.
|
|
63
|
+
const targetMockups = path.resolve(process.cwd(), 'src', 'stories', 'assets', 'mockups');
|
|
64
|
+
fs.mkdirSync(path.dirname(targetMockups), { recursive: true });
|
|
65
|
+
|
|
66
|
+
// Resolves canonical source path for robust symlink target comparisons.
|
|
67
|
+
const resolvedSource = path.resolve(sourceMockups);
|
|
68
|
+
|
|
69
|
+
// Detects whether target path is already a symlink to the expected source directory.
|
|
70
|
+
function isLinkedToSource(targetPath) {
|
|
71
|
+
try {
|
|
72
|
+
// Reads stat metadata to confirm whether target path is a symlink.
|
|
73
|
+
const stat = fs.lstatSync(targetPath);
|
|
74
|
+
if (!stat.isSymbolicLink()) return false;
|
|
75
|
+
// Reads raw symlink destination value.
|
|
76
|
+
const linked = fs.readlinkSync(targetPath);
|
|
77
|
+
// Resolves linked target to absolute path for stable comparison.
|
|
78
|
+
const resolvedLink = path.resolve(path.dirname(targetPath), linked);
|
|
79
|
+
return resolvedLink === resolvedSource;
|
|
80
|
+
} catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Creates or repairs a symlink while safely handling race conditions across parallel executions.
|
|
86
|
+
function createSymlinkSafely(targetPath) {
|
|
87
|
+
try {
|
|
88
|
+
fs.symlinkSync(sourceMockups, targetPath, 'junction');
|
|
89
|
+
return true;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (error?.code !== 'EEXIST') {
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Another process may have created the link after our existence check.
|
|
96
|
+
if (isLinkedToSource(targetPath)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
// Reads current target metadata before replacement.
|
|
102
|
+
const stat = fs.lstatSync(targetPath);
|
|
103
|
+
if (stat.isSymbolicLink()) {
|
|
104
|
+
fs.unlinkSync(targetPath);
|
|
105
|
+
} else {
|
|
106
|
+
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
107
|
+
}
|
|
108
|
+
} catch {
|
|
109
|
+
// If target vanished during a concurrent write, retry create below.
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
fs.symlinkSync(sourceMockups, targetPath, 'junction');
|
|
114
|
+
return true;
|
|
115
|
+
} catch (retryError) {
|
|
116
|
+
if (retryError?.code === 'EEXIST' && isLinkedToSource(targetPath)) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
throw retryError;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (fs.existsSync(targetMockups)) {
|
|
125
|
+
// Reads existing target metadata to decide relink/replace behavior.
|
|
126
|
+
const stat = fs.lstatSync(targetMockups);
|
|
127
|
+
|
|
128
|
+
if (stat.isSymbolicLink()) {
|
|
129
|
+
if (isLinkedToSource(targetMockups)) {
|
|
130
|
+
return { created: false, reason: 'already-linked' };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
fs.unlinkSync(targetMockups);
|
|
134
|
+
// Attempts safe relink after removing stale symlink.
|
|
135
|
+
const created = createSymlinkSafely(targetMockups);
|
|
136
|
+
return { created, reason: created ? 'relinked' : 'already-linked' };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// If a real file or directory is present, replace it so repeated runs stay idempotent.
|
|
140
|
+
fs.rmSync(targetMockups, { recursive: true, force: true });
|
|
141
|
+
// Attempts safe symlink creation after replacement.
|
|
142
|
+
const created = createSymlinkSafely(targetMockups);
|
|
143
|
+
return { created, reason: created ? 'replaced' : 'already-linked' };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Attempts initial symlink creation when no target exists.
|
|
147
|
+
const created = createSymlinkSafely(targetMockups);
|
|
148
|
+
return { created, reason: created ? 'linked' : 'already-linked' };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Ensures runtime favicon assets exist so host app and default about image resolve without 404.
|
|
152
|
+
function ensureRuntimeFaviconAssets() {
|
|
153
|
+
// Resolves package root from this script location.
|
|
154
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
155
|
+
const packageRoot = path.resolve(scriptDir, '..');
|
|
156
|
+
|
|
157
|
+
// Lists favicon assets that should be mirrored from template public assets into runtime public.
|
|
158
|
+
const assets = ['favicon.png', 'favicon.ico'];
|
|
159
|
+
const copied = [];
|
|
160
|
+
const missingSources = [];
|
|
161
|
+
|
|
162
|
+
// Ensures runtime public directory exists before mirroring assets.
|
|
163
|
+
const runtimePublicDir = path.resolve(packageRoot, 'public');
|
|
164
|
+
fs.mkdirSync(runtimePublicDir, { recursive: true });
|
|
165
|
+
|
|
166
|
+
for (const assetName of assets) {
|
|
167
|
+
// Resolves source asset from template files distributed to end users.
|
|
168
|
+
const sourceAsset = path.resolve(packageRoot, 'templates', 'public', assetName);
|
|
169
|
+
if (!fs.existsSync(sourceAsset)) {
|
|
170
|
+
missingSources.push(assetName);
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Resolves runtime mirror destination served at root URL path.
|
|
175
|
+
const targetAsset = path.resolve(runtimePublicDir, assetName);
|
|
176
|
+
if (fs.existsSync(targetAsset)) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
fs.copyFileSync(sourceAsset, targetAsset);
|
|
181
|
+
copied.push(assetName);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
copied,
|
|
186
|
+
missingSources
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Applies all compatibility patches that are required before runtime commands execute.
|
|
191
|
+
export function ensureValenceCompatibility() {
|
|
192
|
+
// Resolves valence package location from supported installation layouts.
|
|
193
|
+
const valenceRoot = resolveValenceRoot();
|
|
194
|
+
if (!valenceRoot) {
|
|
195
|
+
console.warn('Could not locate @portfoliablejs/valence to apply compatibility patches.');
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Creates compatibility CSS entrypoint when missing.
|
|
200
|
+
const indexCreated = ensureValenceIndexCss(valenceRoot);
|
|
201
|
+
if (indexCreated) {
|
|
202
|
+
console.log('Patched valence compatibility: created src/stories/sub-atomic/index.css');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Creates or verifies mockup symlink used by thumbnail catalog.
|
|
206
|
+
const mockupLinkStatus = ensureMockupsSymlink(valenceRoot);
|
|
207
|
+
if (mockupLinkStatus.created) {
|
|
208
|
+
console.log('Patched valence compatibility: linked src/stories/assets/mockups to valence catalog.');
|
|
209
|
+
} else if (mockupLinkStatus.reason === 'missing-source') {
|
|
210
|
+
console.warn('Valence mockup sync skipped: no mockup source found in @portfoliablejs/valence.');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Ensures root-level runtime favicon assets exist for host document and default about image.
|
|
214
|
+
const faviconAssetStatus = ensureRuntimeFaviconAssets();
|
|
215
|
+
if (faviconAssetStatus.copied.length > 0) {
|
|
216
|
+
console.log(`Patched runtime assets: restored ${faviconAssetStatus.copied.join(', ')} from templates/public.`);
|
|
217
|
+
}
|
|
218
|
+
if (faviconAssetStatus.missingSources.length > 0) {
|
|
219
|
+
console.warn(`Runtime favicon sync skipped for missing source assets: ${faviconAssetStatus.missingSources.join(', ')}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// MARK: ENTRYPOINT DETECTION
|
|
224
|
+
// Determines whether this module is being run directly versus imported.
|
|
225
|
+
function isDirectExecution() {
|
|
226
|
+
// Reads the entrypoint argument from current process invocation.
|
|
227
|
+
const entrypoint = process.argv[1];
|
|
228
|
+
if (!entrypoint) return false;
|
|
229
|
+
|
|
230
|
+
// Captures current module URL for direct-execution comparison.
|
|
231
|
+
const currentFileUrl = import.meta.url;
|
|
232
|
+
// Resolves process entrypoint path to URL for module-url equality check.
|
|
233
|
+
const entrypointUrl = pathToFileURL(path.resolve(entrypoint)).href;
|
|
234
|
+
return currentFileUrl === entrypointUrl;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// MARK: SCRIPT ENTRYPOINT
|
|
238
|
+
// Executes compatibility checks only when run as the main process entrypoint.
|
|
239
|
+
if (isDirectExecution()) {
|
|
240
|
+
try {
|
|
241
|
+
ensureValenceCompatibility();
|
|
242
|
+
} catch (error) {
|
|
243
|
+
console.warn('Could not patch valence compatibility:', error?.message || error);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// File: scripts/generate-password-hash.mjs
|
|
3
|
+
// Purpose: Convenience wrapper to run the template password generator from repository root.
|
|
4
|
+
// Author: Lio Schimanko
|
|
5
|
+
|
|
6
|
+
// MARK: IMPORTS
|
|
7
|
+
import { spawnSync } from 'node:child_process';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
|
|
11
|
+
// MARK: PATH RESOLUTION
|
|
12
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const targetScript = path.resolve(__dirname, '../templates/scripts/generate-password-hash.mjs');
|
|
14
|
+
|
|
15
|
+
// MARK: SCRIPT EXECUTION
|
|
16
|
+
const result = spawnSync(process.execPath, [targetScript, ...process.argv.slice(2)], {
|
|
17
|
+
stdio: 'inherit'
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
if (result.error) {
|
|
21
|
+
console.error(`[password-hash] Failed to run generator: ${result.error.message}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
process.exit(result.status ?? 0);
|