@donotdev/cli 0.0.5 → 0.0.6
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/dependencies-matrix.json +57 -33
- package/dist/bin/commands/build.js +9 -3
- package/dist/bin/commands/bump.js +19 -7
- package/dist/bin/commands/cacheout.js +9 -3
- package/dist/bin/commands/create-app.js +21 -7
- package/dist/bin/commands/create-project.js +22 -7
- package/dist/bin/commands/deploy.js +22 -14
- package/dist/bin/commands/dev.js +9 -3
- package/dist/bin/commands/emu.js +9 -3
- package/dist/bin/commands/format.js +9 -3
- package/dist/bin/commands/lint.js +9 -3
- package/dist/bin/commands/make-admin.d.ts +11 -0
- package/dist/bin/commands/make-admin.d.ts.map +1 -0
- package/dist/bin/commands/make-admin.js +12 -0
- package/dist/bin/commands/make-admin.js.map +1 -0
- package/dist/bin/commands/preview.js +9 -3
- package/dist/bin/commands/sync-secrets.js +9 -3
- package/dist/index.js +33 -17
- package/package.json +1 -1
- package/templates/app-demo/index.html.example +4 -0
- package/templates/app-demo/src/App.tsx.example +28 -10
- package/templates/app-demo/src/config/app.ts.example +56 -0
- package/templates/app-next/src/app/ClientLayout.tsx.example +4 -3
- package/templates/app-next/src/app/layout.tsx.example +17 -25
- package/templates/app-next/src/globals.css.example +10 -7
- package/templates/app-next/src/locales/dndev_en.json.example +68 -0
- package/templates/app-next/src/pages/locales/example_en.json.example +5 -0
- package/templates/app-vite/index.html.example +3 -0
- package/templates/app-vite/src/globals.css.example +14 -6
- package/templates/app-vite/src/locales/dndev_en.json.example +68 -0
- package/templates/functions-firebase/README.md.example +25 -0
- package/templates/functions-firebase/tsconfig.json.example +3 -13
- package/templates/functions-vercel/tsconfig.json.example +1 -13
- package/templates/root-consumer/firebase.json.example +1 -1
- package/templates/root-consumer/guides/COMPONENTS_ADV.md.example +456 -360
- package/templates/root-consumer/guides/COMPONENTS_ATOMIC.md.example +42 -0
- package/templates/root-consumer/guides/INDEX.md.example +3 -0
- package/templates/root-consumer/guides/SETUP_APP_CONFIG.md.example +5 -2
- package/templates/root-consumer/guides/SETUP_BILLING.md.example +44 -4
- package/templates/root-consumer/guides/SETUP_CRUD.md.example +1244 -0
- package/templates/root-consumer/guides/SETUP_FUNCTIONS.md.example +52 -0
- package/templates/root-consumer/guides/SETUP_PAGES.md.example +17 -0
- package/templates/root-consumer/guides/SETUP_PWA.md.example +213 -0
- package/templates/root-consumer/guides/USE_ROUTING.md.example +503 -0
- package/templates/root-consumer/vercel.json.example +315 -20
- package/templates/app-demo/src/Routes.tsx.example +0 -20
- package/templates/app-vite/src/Routes.tsx.example +0 -16
- package/templates/app-vite/src/pages/locales/README.md.example +0 -1
|
@@ -7141,11 +7141,17 @@ var init_PathResolver = __esm({
|
|
|
7141
7141
|
/**
|
|
7142
7142
|
* Get path to empty.js module for optional dependency aliasing
|
|
7143
7143
|
* Used by Vite and Turbopack to alias missing optional deps
|
|
7144
|
-
* @returns
|
|
7144
|
+
* @param returnPackageSpecifier - If true, returns package specifier '@donotdev/core/empty' for Turbopack. If false, returns absolute path for Vite.
|
|
7145
|
+
* @returns Package specifier or absolute path to empty.js
|
|
7145
7146
|
*/
|
|
7146
|
-
getEmptyModulePath() {
|
|
7147
|
+
getEmptyModulePath(returnPackageSpecifier = false) {
|
|
7148
|
+
if (returnPackageSpecifier) {
|
|
7149
|
+
return "@donotdev/core/empty";
|
|
7150
|
+
}
|
|
7151
|
+
const resolved = this.resolvePackage("@donotdev/core/empty");
|
|
7152
|
+
if (resolved) return resolved;
|
|
7147
7153
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
7148
|
-
return this.normalizePath(join(thisDir, "../
|
|
7154
|
+
return this.normalizePath(join(thisDir, "../empty.js"));
|
|
7149
7155
|
}
|
|
7150
7156
|
// === PRIVATE METHODS ===
|
|
7151
7157
|
/**
|
|
@@ -7141,11 +7141,17 @@ var init_PathResolver = __esm({
|
|
|
7141
7141
|
/**
|
|
7142
7142
|
* Get path to empty.js module for optional dependency aliasing
|
|
7143
7143
|
* Used by Vite and Turbopack to alias missing optional deps
|
|
7144
|
-
* @returns
|
|
7144
|
+
* @param returnPackageSpecifier - If true, returns package specifier '@donotdev/core/empty' for Turbopack. If false, returns absolute path for Vite.
|
|
7145
|
+
* @returns Package specifier or absolute path to empty.js
|
|
7145
7146
|
*/
|
|
7146
|
-
getEmptyModulePath() {
|
|
7147
|
+
getEmptyModulePath(returnPackageSpecifier = false) {
|
|
7148
|
+
if (returnPackageSpecifier) {
|
|
7149
|
+
return "@donotdev/core/empty";
|
|
7150
|
+
}
|
|
7151
|
+
const resolved = this.resolvePackage("@donotdev/core/empty");
|
|
7152
|
+
if (resolved) return resolved;
|
|
7147
7153
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
7148
|
-
return this.normalizePath(join(thisDir, "../
|
|
7154
|
+
return this.normalizePath(join(thisDir, "../empty.js"));
|
|
7149
7155
|
}
|
|
7150
7156
|
// === PRIVATE METHODS ===
|
|
7151
7157
|
/**
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Make Admin Command Wrapper
|
|
3
|
+
* @description Re-exports makeAdmin from tooling for CLI bundling.
|
|
4
|
+
* Each command wrapper is bundled separately as a fully self-contained script.
|
|
5
|
+
*
|
|
6
|
+
* @version 0.0.1
|
|
7
|
+
* @since 0.0.1
|
|
8
|
+
* @author AMBROISE PARK Consulting
|
|
9
|
+
*/
|
|
10
|
+
export { makeAdmin as main } from '@donotdev/tooling';
|
|
11
|
+
//# sourceMappingURL=make-admin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"make-admin.d.ts","sourceRoot":"","sources":["../../../src/bin/commands/make-admin.ts"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,IAAI,IAAI,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// packages/cli/src/bin/commands/make-admin.ts
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Make Admin Command Wrapper
|
|
4
|
+
* @description Re-exports makeAdmin from tooling for CLI bundling.
|
|
5
|
+
* Each command wrapper is bundled separately as a fully self-contained script.
|
|
6
|
+
*
|
|
7
|
+
* @version 0.0.1
|
|
8
|
+
* @since 0.0.1
|
|
9
|
+
* @author AMBROISE PARK Consulting
|
|
10
|
+
*/
|
|
11
|
+
export { makeAdmin as main } from '@donotdev/tooling';
|
|
12
|
+
//# sourceMappingURL=make-admin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"make-admin.js","sourceRoot":"","sources":["../../../src/bin/commands/make-admin.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,IAAI,IAAI,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -7439,11 +7439,17 @@ var init_PathResolver = __esm({
|
|
|
7439
7439
|
/**
|
|
7440
7440
|
* Get path to empty.js module for optional dependency aliasing
|
|
7441
7441
|
* Used by Vite and Turbopack to alias missing optional deps
|
|
7442
|
-
* @returns
|
|
7442
|
+
* @param returnPackageSpecifier - If true, returns package specifier '@donotdev/core/empty' for Turbopack. If false, returns absolute path for Vite.
|
|
7443
|
+
* @returns Package specifier or absolute path to empty.js
|
|
7443
7444
|
*/
|
|
7444
|
-
getEmptyModulePath() {
|
|
7445
|
+
getEmptyModulePath(returnPackageSpecifier = false) {
|
|
7446
|
+
if (returnPackageSpecifier) {
|
|
7447
|
+
return "@donotdev/core/empty";
|
|
7448
|
+
}
|
|
7449
|
+
const resolved = this.resolvePackage("@donotdev/core/empty");
|
|
7450
|
+
if (resolved) return resolved;
|
|
7445
7451
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
7446
|
-
return this.normalizePath(join(thisDir, "../
|
|
7452
|
+
return this.normalizePath(join(thisDir, "../empty.js"));
|
|
7447
7453
|
}
|
|
7448
7454
|
// === PRIVATE METHODS ===
|
|
7449
7455
|
/**
|
|
@@ -7463,11 +7463,17 @@ var init_PathResolver = __esm({
|
|
|
7463
7463
|
/**
|
|
7464
7464
|
* Get path to empty.js module for optional dependency aliasing
|
|
7465
7465
|
* Used by Vite and Turbopack to alias missing optional deps
|
|
7466
|
-
* @returns
|
|
7466
|
+
* @param returnPackageSpecifier - If true, returns package specifier '@donotdev/core/empty' for Turbopack. If false, returns absolute path for Vite.
|
|
7467
|
+
* @returns Package specifier or absolute path to empty.js
|
|
7467
7468
|
*/
|
|
7468
|
-
getEmptyModulePath() {
|
|
7469
|
+
getEmptyModulePath(returnPackageSpecifier = false) {
|
|
7470
|
+
if (returnPackageSpecifier) {
|
|
7471
|
+
return "@donotdev/core/empty";
|
|
7472
|
+
}
|
|
7473
|
+
const resolved = this.resolvePackage("@donotdev/core/empty");
|
|
7474
|
+
if (resolved) return resolved;
|
|
7469
7475
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
7470
|
-
return this.normalizePath(join(thisDir, "../
|
|
7476
|
+
return this.normalizePath(join(thisDir, "../empty.js"));
|
|
7471
7477
|
}
|
|
7472
7478
|
// === PRIVATE METHODS ===
|
|
7473
7479
|
/**
|
package/dist/index.js
CHANGED
|
@@ -7762,11 +7762,17 @@ var init_PathResolver = __esm({
|
|
|
7762
7762
|
/**
|
|
7763
7763
|
* Get path to empty.js module for optional dependency aliasing
|
|
7764
7764
|
* Used by Vite and Turbopack to alias missing optional deps
|
|
7765
|
-
* @returns
|
|
7765
|
+
* @param returnPackageSpecifier - If true, returns package specifier '@donotdev/core/empty' for Turbopack. If false, returns absolute path for Vite.
|
|
7766
|
+
* @returns Package specifier or absolute path to empty.js
|
|
7766
7767
|
*/
|
|
7767
|
-
getEmptyModulePath() {
|
|
7768
|
+
getEmptyModulePath(returnPackageSpecifier = false) {
|
|
7769
|
+
if (returnPackageSpecifier) {
|
|
7770
|
+
return "@donotdev/core/empty";
|
|
7771
|
+
}
|
|
7772
|
+
const resolved = this.resolvePackage("@donotdev/core/empty");
|
|
7773
|
+
if (resolved) return resolved;
|
|
7768
7774
|
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
7769
|
-
return this.normalizePath(join(thisDir, "../
|
|
7775
|
+
return this.normalizePath(join(thisDir, "../empty.js"));
|
|
7770
7776
|
}
|
|
7771
7777
|
// === PRIVATE METHODS ===
|
|
7772
7778
|
/**
|
|
@@ -8772,12 +8778,7 @@ function executeFirebaseCommand(args, options) {
|
|
|
8772
8778
|
env: deployEnv
|
|
8773
8779
|
};
|
|
8774
8780
|
let result;
|
|
8775
|
-
|
|
8776
|
-
const firebaseCmd = "C:\\Program Files\\nodejs\\firebase.cmd";
|
|
8777
|
-
result = spawnSync2(firebaseCmd, args, { ...spawnOptions, shell: false });
|
|
8778
|
-
} else {
|
|
8779
|
-
result = spawnSync2("firebase", args, { ...spawnOptions, shell: true });
|
|
8780
|
-
}
|
|
8781
|
+
result = spawnSync2("firebase", args, { ...spawnOptions, shell: true });
|
|
8781
8782
|
if (result.error) {
|
|
8782
8783
|
return {
|
|
8783
8784
|
success: false,
|
|
@@ -10376,11 +10377,15 @@ async function deployFunctions(appDir, serviceAccountPath, projectId, config) {
|
|
|
10376
10377
|
}
|
|
10377
10378
|
const s = Y2();
|
|
10378
10379
|
s.start("Validating function dependencies...");
|
|
10379
|
-
const nodeModulesPath = joinPath(functionsDir, "node_modules");
|
|
10380
10380
|
const requiredPackages = ["firebase-functions", "firebase-admin"];
|
|
10381
|
-
const missingPackages = requiredPackages.filter(
|
|
10382
|
-
|
|
10383
|
-
|
|
10381
|
+
const missingPackages = requiredPackages.filter((pkg) => {
|
|
10382
|
+
try {
|
|
10383
|
+
resolvePackage(pkg, functionsDir);
|
|
10384
|
+
return false;
|
|
10385
|
+
} catch {
|
|
10386
|
+
return true;
|
|
10387
|
+
}
|
|
10388
|
+
});
|
|
10384
10389
|
if (missingPackages.length > 0) {
|
|
10385
10390
|
s.stop("Missing dependencies");
|
|
10386
10391
|
throw new DoNotDevError(
|
|
@@ -10395,6 +10400,8 @@ To fix this, run:
|
|
|
10395
10400
|
}
|
|
10396
10401
|
s.stop("Dependencies validated");
|
|
10397
10402
|
prepareFunctionsForDeployment(functionsDir, config.verbose);
|
|
10403
|
+
log.debug("Waiting for file system sync...");
|
|
10404
|
+
await new Promise((resolve4) => setTimeout(resolve4, 1e3));
|
|
10398
10405
|
try {
|
|
10399
10406
|
if (!config.skipBuild) {
|
|
10400
10407
|
const s2 = Y2();
|
|
@@ -11125,7 +11132,7 @@ function generateScripts(templateName, options) {
|
|
|
11125
11132
|
const scripts = {};
|
|
11126
11133
|
if (templateName.includes("vite")) {
|
|
11127
11134
|
scripts.dev = "vite";
|
|
11128
|
-
scripts.build = "
|
|
11135
|
+
scripts.build = "vite build";
|
|
11129
11136
|
scripts.preview = "vite preview";
|
|
11130
11137
|
scripts.lint = "eslint src/";
|
|
11131
11138
|
scripts["type-check"] = "tsc --noEmit";
|
|
@@ -11161,7 +11168,9 @@ function generateScripts(templateName, options) {
|
|
|
11161
11168
|
function generatePackageJson(templateName, mode, options = {}) {
|
|
11162
11169
|
const matrixResult = loadMatrix(mode);
|
|
11163
11170
|
if (!matrixResult) {
|
|
11164
|
-
throw new Error(
|
|
11171
|
+
throw new Error(
|
|
11172
|
+
"dependencies-matrix.json not found. This command requires the matrix file."
|
|
11173
|
+
);
|
|
11165
11174
|
}
|
|
11166
11175
|
const { matrix, cliVersion } = matrixResult;
|
|
11167
11176
|
const template = matrix.templateMapping?.[templateName];
|
|
@@ -11221,6 +11230,11 @@ function generatePackageJson(templateName, mode, options = {}) {
|
|
|
11221
11230
|
result.main = "./index.ts";
|
|
11222
11231
|
result.types = "./index.ts";
|
|
11223
11232
|
}
|
|
11233
|
+
if (templateName.includes("vite") || templateName.includes("nextjs") || templateName.includes("functions")) {
|
|
11234
|
+
if (!dependencies.entities) {
|
|
11235
|
+
dependencies.entities = "workspace:*";
|
|
11236
|
+
}
|
|
11237
|
+
}
|
|
11224
11238
|
if (templateName.includes("functions")) {
|
|
11225
11239
|
result.engines = { node: "20" };
|
|
11226
11240
|
if (options.appName) {
|
|
@@ -11343,7 +11357,8 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
11343
11357
|
firebaseProjectId: appName.toLowerCase(),
|
|
11344
11358
|
firebaseSecretName: appName.toUpperCase().replace(/-/g, "_"),
|
|
11345
11359
|
monorepoRelativePath: "../../packages/tooling",
|
|
11346
|
-
appTemplate
|
|
11360
|
+
appTemplate,
|
|
11361
|
+
isNextjs: appTemplate === "nextjs"
|
|
11347
11362
|
};
|
|
11348
11363
|
const templateSourceDir = joinPath(templatesRoot, templateDir);
|
|
11349
11364
|
const templateFiles = await glob("**/*", {
|
|
@@ -11436,7 +11451,7 @@ async function createApp(appName, appConfig, workspaceRoot, templatesRoot) {
|
|
|
11436
11451
|
await replacePlaceholders(firebaseJsonDest, replacements);
|
|
11437
11452
|
}
|
|
11438
11453
|
}
|
|
11439
|
-
if (appConfig.needsBackend && appConfig.backendPlatform === "vercel") {
|
|
11454
|
+
if (appTemplate === "nextjs" || appConfig.needsBackend && appConfig.backendPlatform === "vercel") {
|
|
11440
11455
|
const vercelJsonSource = joinPath(
|
|
11441
11456
|
deploymentTemplateDir,
|
|
11442
11457
|
"vercel.json.example"
|
|
@@ -11817,6 +11832,7 @@ async function main8(options) {
|
|
|
11817
11832
|
setupGithubActions: false,
|
|
11818
11833
|
monorepoRelativePath: relativeMonorepoPath,
|
|
11819
11834
|
appTemplate: "vite",
|
|
11835
|
+
isNextjs: false,
|
|
11820
11836
|
firebaseProjectId: projectName.toLowerCase(),
|
|
11821
11837
|
firebaseSecretName: projectName.toUpperCase().replace(/-/g, "_"),
|
|
11822
11838
|
needsAuth,
|
package/package.json
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>DoNotDev Components Demo</title>
|
|
7
|
+
|
|
8
|
+
<!-- ✅ PERFORMANCE: Critical font preloads (loads before CSS) -->
|
|
9
|
+
<link rel="preload" href="/fonts/Inter-latin.woff2" as="font" type="font/woff2" crossorigin="anonymous">
|
|
10
|
+
|
|
7
11
|
<style>
|
|
8
12
|
body {
|
|
9
13
|
margin: 0;
|
|
@@ -1,17 +1,35 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Main application component
|
|
3
|
+
* @description Root application component with configuration-driven architecture
|
|
4
|
+
* @version 0.0.4
|
|
5
|
+
* @since 0.0.1
|
|
6
|
+
* @author AMBROISE PARK Consulting
|
|
7
|
+
*/
|
|
2
8
|
|
|
3
|
-
import {
|
|
4
|
-
import { TooltipProvider, Toaster } from '@donotdev/components';
|
|
9
|
+
import { ViteAppProviders } from '@donotdev/ui/vite';
|
|
5
10
|
|
|
6
|
-
import {
|
|
11
|
+
import { appConfig } from './config/app';
|
|
7
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Main application component
|
|
15
|
+
*
|
|
16
|
+
* Configuration-driven architecture with smart defaults.
|
|
17
|
+
* Layout preset comes from appConfig.preset (configured in config/app.ts).
|
|
18
|
+
* Routes are automatically discovered from src/pages/*Page.tsx files.
|
|
19
|
+
*
|
|
20
|
+
* Note: HomePage.tsx in src/pages/ is automatically used for the root route "/"
|
|
21
|
+
*
|
|
22
|
+
* @version 0.0.4
|
|
23
|
+
* @since 0.0.1
|
|
24
|
+
* @author AMBROISE PARK Consulting
|
|
25
|
+
*/
|
|
8
26
|
export function App() {
|
|
9
27
|
return (
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
28
|
+
<ViteAppProviders
|
|
29
|
+
config={appConfig}
|
|
30
|
+
layout={{
|
|
31
|
+
breadcrumbs: 'smart', // 'smart' | 'always' | 'never'
|
|
32
|
+
}}
|
|
33
|
+
/>
|
|
16
34
|
);
|
|
17
35
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Application configuration
|
|
3
|
+
* @description Defines application metadata and feature flags
|
|
4
|
+
* @version 0.0.1
|
|
5
|
+
* @since 0.0.1
|
|
6
|
+
* @author AMBROISE PARK Consulting
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { AppConfig } from '@donotdev/core';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Application name constant
|
|
13
|
+
*
|
|
14
|
+
* @version 0.0.1
|
|
15
|
+
* @since 0.0.1
|
|
16
|
+
* @author AMBROISE PARK Consulting
|
|
17
|
+
*/
|
|
18
|
+
export const APP_NAME = '{{appName}}';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Application short name constant
|
|
22
|
+
*
|
|
23
|
+
* @version 0.0.1
|
|
24
|
+
* @since 0.0.1
|
|
25
|
+
* @author AMBROISE PARK Consulting
|
|
26
|
+
*/
|
|
27
|
+
export const APP_SHORT_NAME = '{{appShortName}}';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Application description constant
|
|
31
|
+
*
|
|
32
|
+
* @version 0.0.1
|
|
33
|
+
* @since 0.0.1
|
|
34
|
+
* @author AMBROISE PARK Consulting
|
|
35
|
+
*/
|
|
36
|
+
export const APP_DESCRIPTION = 'DoNotDev Components Demo';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Application configuration object
|
|
40
|
+
*
|
|
41
|
+
* @version 0.0.1
|
|
42
|
+
* @since 0.0.1
|
|
43
|
+
* @author AMBROISE PARK Consulting
|
|
44
|
+
*/
|
|
45
|
+
export const appConfig: AppConfig = {
|
|
46
|
+
app: {
|
|
47
|
+
name: APP_NAME,
|
|
48
|
+
shortName: APP_SHORT_NAME,
|
|
49
|
+
description: APP_DESCRIPTION,
|
|
50
|
+
},
|
|
51
|
+
preset: 'landing', // 'landing' | 'admin' | 'moolti' | 'docs' | 'blog' | 'game' | 'plain'
|
|
52
|
+
features: {
|
|
53
|
+
// Optional: Enable debug tools in development
|
|
54
|
+
// debug: true,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
@@ -6,7 +6,6 @@ import { appConfig } from '../config/app';
|
|
|
6
6
|
|
|
7
7
|
interface ClientLayoutProps {
|
|
8
8
|
children: React.ReactNode;
|
|
9
|
-
serverCookies?: string;
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
/**
|
|
@@ -17,13 +16,14 @@ interface ClientLayoutProps {
|
|
|
17
16
|
* Layout prop is for breadcrumbs and slot customizations only.
|
|
18
17
|
* CSS variable overrides should be done in themes.css.
|
|
19
18
|
* Override only what you need - everything else has sensible defaults.
|
|
19
|
+
*
|
|
20
|
+
* Theme detection happens client-side to avoid Next.js 16 race condition.
|
|
20
21
|
*/
|
|
21
|
-
export function ClientLayout({ children
|
|
22
|
+
export function ClientLayout({ children }: ClientLayoutProps) {
|
|
22
23
|
return (
|
|
23
24
|
<div id="root">
|
|
24
25
|
<NextJsAppProviders
|
|
25
26
|
config={appConfig}
|
|
26
|
-
serverCookies={serverCookies}
|
|
27
27
|
layout={{
|
|
28
28
|
breadcrumbs: 'smart', // 'smart' | 'always' | 'never'
|
|
29
29
|
header: {
|
|
@@ -37,3 +37,4 @@ export function ClientLayout({ children, serverCookies }: ClientLayoutProps) {
|
|
|
37
37
|
</div>
|
|
38
38
|
);
|
|
39
39
|
}
|
|
40
|
+
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import type { Metadata } from 'next';
|
|
2
|
-
import { cookies } from 'next/headers';
|
|
3
2
|
import '../globals.css';
|
|
4
3
|
import { ClientLayout } from './ClientLayout';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Framework fonts are loaded via CSS @font-face rules in dndev.css
|
|
8
7
|
*
|
|
9
|
-
* The framework CSS includes optimized @font-face declarations for
|
|
10
|
-
* with proper unicode-range subsetting for performance.
|
|
8
|
+
* The framework CSS includes optimized @font-face declarations for Inter
|
|
9
|
+
* (default) and Roboto (optional) with proper unicode-range subsetting for performance.
|
|
11
10
|
* Fonts are copied from framework to public/fonts/ by AssetDiscovery during build.
|
|
12
11
|
*/
|
|
13
12
|
|
|
@@ -16,36 +15,29 @@ export const metadata: Metadata = {
|
|
|
16
15
|
description: 'Next.js site using DNDev framework',
|
|
17
16
|
};
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Root layout for Next.js App Router
|
|
20
|
+
*
|
|
21
|
+
* Theme detection happens client-side via ClientLayout to avoid
|
|
22
|
+
* Next.js 16 + Turbopack race condition with async layouts.
|
|
23
|
+
* Brief FOUC possible on first load, but prevents build-manifest.json errors.
|
|
24
|
+
*/
|
|
25
|
+
export default function RootLayout({
|
|
20
26
|
children,
|
|
21
27
|
}: {
|
|
22
28
|
children: React.ReactNode;
|
|
23
29
|
}) {
|
|
24
|
-
/**
|
|
25
|
-
* SSR: Read theme from cookies to apply correct theme class to HTML
|
|
26
|
-
*
|
|
27
|
-
* This ensures the server-rendered HTML has the correct theme class,
|
|
28
|
-
* preventing FOUC (Flash of Unstyled Content) when client hydrates.
|
|
29
|
-
*
|
|
30
|
-
* Flow:
|
|
31
|
-
* 1. Server reads cookie → applies theme class to <html>/<body>
|
|
32
|
-
* 2. NextJsStoresInitializer hydrates store from cookie
|
|
33
|
-
* 3. Client hydrates → Zustand persist matches server state
|
|
34
|
-
* 4. setAvailableThemes() ensures DOM matches persisted theme
|
|
35
|
-
*/
|
|
36
|
-
const cookieStore = await cookies();
|
|
37
|
-
const themeCookie = cookieStore.get('dndev-theme');
|
|
38
|
-
const themeClass = themeCookie?.value || 'light';
|
|
39
|
-
|
|
40
30
|
return (
|
|
41
|
-
<html
|
|
31
|
+
<html
|
|
32
|
+
lang="en"
|
|
33
|
+
className="dndev-design-tokens light"
|
|
34
|
+
suppressHydrationWarning
|
|
35
|
+
>
|
|
42
36
|
<head>
|
|
43
37
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
44
38
|
</head>
|
|
45
|
-
<body className=
|
|
46
|
-
<ClientLayout
|
|
47
|
-
{children}
|
|
48
|
-
</ClientLayout>
|
|
39
|
+
<body className="light">
|
|
40
|
+
<ClientLayout>{children}</ClientLayout>
|
|
49
41
|
</body>
|
|
50
42
|
</html>
|
|
51
43
|
);
|
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* Font configuration
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
* Framework defaults to Inter (defined in @donotdev/components/styles/variables.css).
|
|
10
|
+
* Fonts are loaded via CSS @font-face rules in @donotdev/ui/assets/fonts/fonts.css
|
|
11
|
+
* and copied to public/fonts/ by AssetDiscovery during build.
|
|
12
|
+
*
|
|
13
|
+
* To override, uncomment and customize:
|
|
14
|
+
* :root {
|
|
15
|
+
* --font-sans: 'YourFont', sans-serif;
|
|
16
|
+
* --font-family: var(--font-sans);
|
|
17
|
+
* }
|
|
18
|
+
*/
|
|
@@ -496,6 +496,10 @@
|
|
|
496
496
|
"copied": "Copied!",
|
|
497
497
|
"copyCodeToClipboard": "Copy code to clipboard"
|
|
498
498
|
},
|
|
499
|
+
"video": {
|
|
500
|
+
"clickToWatch": "Click to watch video",
|
|
501
|
+
"watchVideo": "Watch Video"
|
|
502
|
+
},
|
|
499
503
|
"inspector": {
|
|
500
504
|
"pageSource": "Page Source",
|
|
501
505
|
"inspectPageSourceCode": "Inspect page source code"
|
|
@@ -512,5 +516,69 @@
|
|
|
512
516
|
"goHome": "Go Home",
|
|
513
517
|
"goBack": "Go Back",
|
|
514
518
|
"contactSupport": "If the problem persists, please contact support."
|
|
519
|
+
},
|
|
520
|
+
"redirectOverlay": {
|
|
521
|
+
"phases": {
|
|
522
|
+
"connecting": "Connecting to secure server...",
|
|
523
|
+
"preparing": "Preparing your session...",
|
|
524
|
+
"redirecting": "Redirecting...",
|
|
525
|
+
"timeout": "Taking longer than expected..."
|
|
526
|
+
},
|
|
527
|
+
"cancel": "Cancel",
|
|
528
|
+
"doNotRefresh": "Please do not refresh or press back",
|
|
529
|
+
"default": {
|
|
530
|
+
"title": "Redirecting",
|
|
531
|
+
"message": "Please wait while we redirect you...",
|
|
532
|
+
"subtitle": "This may take a moment",
|
|
533
|
+
"ariaLabel": "Redirecting, please wait"
|
|
534
|
+
},
|
|
535
|
+
"stripe-checkout": {
|
|
536
|
+
"title": "Redirecting to Stripe",
|
|
537
|
+
"message": "Initializing secure payment...",
|
|
538
|
+
"subtitle": "This may take a moment on first load",
|
|
539
|
+
"ariaLabel": "Preparing secure checkout, please wait"
|
|
540
|
+
},
|
|
541
|
+
"stripe-portal": {
|
|
542
|
+
"title": "Redirecting to Stripe",
|
|
543
|
+
"message": "Opening billing portal...",
|
|
544
|
+
"subtitle": "Manage your subscription and invoices",
|
|
545
|
+
"ariaLabel": "Opening billing portal, please wait"
|
|
546
|
+
},
|
|
547
|
+
"oauth-google": {
|
|
548
|
+
"title": "Signing in with Google",
|
|
549
|
+
"message": "Connecting to Google...",
|
|
550
|
+
"subtitle": "You will be redirected to sign in",
|
|
551
|
+
"ariaLabel": "Signing in with Google, please wait"
|
|
552
|
+
},
|
|
553
|
+
"oauth-github": {
|
|
554
|
+
"title": "Signing in with GitHub",
|
|
555
|
+
"message": "Connecting to GitHub...",
|
|
556
|
+
"subtitle": "You will be redirected to sign in",
|
|
557
|
+
"ariaLabel": "Signing in with GitHub, please wait"
|
|
558
|
+
},
|
|
559
|
+
"oauth-apple": {
|
|
560
|
+
"title": "Signing in with Apple",
|
|
561
|
+
"message": "Connecting to Apple...",
|
|
562
|
+
"subtitle": "You will be redirected to sign in",
|
|
563
|
+
"ariaLabel": "Signing in with Apple, please wait"
|
|
564
|
+
},
|
|
565
|
+
"oauth-microsoft": {
|
|
566
|
+
"title": "Signing in with Microsoft",
|
|
567
|
+
"message": "Connecting to Microsoft...",
|
|
568
|
+
"subtitle": "You will be redirected to sign in",
|
|
569
|
+
"ariaLabel": "Signing in with Microsoft, please wait"
|
|
570
|
+
},
|
|
571
|
+
"oauth-facebook": {
|
|
572
|
+
"title": "Signing in with Facebook",
|
|
573
|
+
"message": "Connecting to Facebook...",
|
|
574
|
+
"subtitle": "You will be redirected to sign in",
|
|
575
|
+
"ariaLabel": "Signing in with Facebook, please wait"
|
|
576
|
+
},
|
|
577
|
+
"auth-email-link": {
|
|
578
|
+
"title": "Sending verification",
|
|
579
|
+
"message": "Preparing your verification link...",
|
|
580
|
+
"subtitle": "Check your email inbox",
|
|
581
|
+
"ariaLabel": "Sending verification email, please wait"
|
|
582
|
+
}
|
|
515
583
|
}
|
|
516
584
|
}
|
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
<title>Loading...</title>
|
|
31
31
|
<meta name="description" content="Modern web application powered by DoNotDev" />
|
|
32
32
|
|
|
33
|
+
<!-- ✅ PERFORMANCE: Critical font preloads (loads before CSS) -->
|
|
34
|
+
<!-- Only preload fonts with font-display: swap (not optional) -->
|
|
35
|
+
<!-- Example: <link rel="preload" href="/fonts/YourFont.woff2" as="font" type="font/woff2" crossorigin="anonymous"> -->
|
|
33
36
|
|
|
34
37
|
<!-- ✅ PERFORMANCE: Critical CSS inlined here by build -->
|
|
35
38
|
<style>
|
|
@@ -3,9 +3,17 @@
|
|
|
3
3
|
|
|
4
4
|
@import './themes.css';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Font configuration
|
|
8
|
+
*
|
|
9
|
+
* Framework defaults to Inter (defined in @donotdev/components/styles/variables.css).
|
|
10
|
+
* Fonts are loaded via CSS @font-face rules in @donotdev/ui/assets/fonts/fonts.css
|
|
11
|
+
* and copied to public/fonts/ by AssetDiscovery during build.
|
|
12
|
+
*
|
|
13
|
+
* To override, uncomment and customize:
|
|
14
|
+
* :root {
|
|
15
|
+
* --font-sans: 'YourFont', sans-serif;
|
|
16
|
+
* --font-mono: 'YourMonoFont', monospace;
|
|
17
|
+
* --font-family: var(--font-sans);
|
|
18
|
+
* }
|
|
19
|
+
*/
|