dh-remixer-sdk 0.0.23 → 0.0.24-10fa10f
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/package.json +14 -7
- package/scripts/ssg-helmet/fs-utils.mjs +4 -4
- package/scripts/ssg-helmet/index.mjs +37 -6
- package/scripts/ssg-helmet/inject-unpack-redirect.mjs +4 -13
- package/scripts/ssg-helmet/prerenderer.mjs +179 -55
- package/scripts/ssg-helmet/vars.mjs +10 -5
- package/templates/base/package.json +1 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dh-remixer-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.24-10fa10f",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"templates",
|
|
@@ -12,11 +12,18 @@
|
|
|
12
12
|
"sdk-update": "./scripts/sdk-update/index.mjs"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"sucrase": "
|
|
15
|
+
"acorn": "8.14.0",
|
|
16
|
+
"acorn-jsx": "5.3.2",
|
|
17
|
+
"chrome-launcher": "1.2.1",
|
|
18
|
+
"chrome-remote-interface": "0.34.0",
|
|
19
|
+
"estree-walker": "3.0.3",
|
|
20
|
+
"sucrase": "3.35.0",
|
|
21
|
+
"@sparticuz/chromium": "143.0.4"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"eslint-plugin-n": "17.24.0",
|
|
25
|
+
"globals": "17.4.0",
|
|
26
|
+
"eslint": "9.0.0",
|
|
27
|
+
"eslint-plugin-import": "2.32.0"
|
|
21
28
|
}
|
|
22
29
|
}
|
|
@@ -2,18 +2,18 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { injectUnpackRedirectScript } from "./inject-unpack-redirect.mjs";
|
|
4
4
|
import { buildRedirectHelmetHtml } from "./build-helmet-html.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { ROOT_ROUTE, REMIXER_BUILD_DIR } from "./vars.mjs";
|
|
6
6
|
|
|
7
7
|
async function writeFile(route, content) {
|
|
8
|
-
const productOut = path.join(
|
|
8
|
+
const productOut = path.join(REMIXER_BUILD_DIR, route, "index.html");
|
|
9
9
|
await fs.mkdir(path.dirname(productOut), { recursive: true });
|
|
10
10
|
await fs.writeFile(productOut, content, "utf8");
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export async function writeHelmetFile(html, currentRoute, isIndexable) {
|
|
14
14
|
const idxTag = isIndexable ? "indexable" : "not_indexable";
|
|
15
|
-
if (currentRoute ===
|
|
16
|
-
await injectUnpackRedirectScript(
|
|
15
|
+
if (currentRoute === ROOT_ROUTE) {
|
|
16
|
+
await injectUnpackRedirectScript();
|
|
17
17
|
console.log(`[crawled][${idxTag}][edited] ${currentRoute}index.html`);
|
|
18
18
|
return;
|
|
19
19
|
}
|
|
@@ -12,11 +12,11 @@ import {
|
|
|
12
12
|
import { buildSitemapXml } from "./build-sitemap-xml.mjs";
|
|
13
13
|
import { writeHelmetFile } from "./fs-utils.mjs";
|
|
14
14
|
import {
|
|
15
|
-
|
|
15
|
+
ROOT_ROUTE,
|
|
16
16
|
IS_INDEXABLE,
|
|
17
17
|
IS_NOT_INDEXABLE,
|
|
18
18
|
TARGET_DOMAIN,
|
|
19
|
-
|
|
19
|
+
REMIXER_BUILD_DIR,
|
|
20
20
|
} from "./vars.mjs";
|
|
21
21
|
|
|
22
22
|
/**
|
|
@@ -28,11 +28,42 @@ import {
|
|
|
28
28
|
|
|
29
29
|
async function main() {
|
|
30
30
|
const visistedRoutes = [];
|
|
31
|
-
let queuedRoutes = [
|
|
31
|
+
let queuedRoutes = [ROOT_ROUTE];
|
|
32
32
|
const reactRoutes = await getRoutes();
|
|
33
|
-
let sitemapRoutes = [
|
|
33
|
+
let sitemapRoutes = [ROOT_ROUTE, reactRoutes.find((i) => typeof i === "object")];
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
// Retry logic with proper cleanup on failure
|
|
36
|
+
let prerenderer = null;
|
|
37
|
+
const maxRetries = 3;
|
|
38
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
39
|
+
try {
|
|
40
|
+
console.log(`[ssg-helmet] Creating Prerenderer (attempt ${attempt}/${maxRetries})...`);
|
|
41
|
+
prerenderer = await Prerenderer.create();
|
|
42
|
+
console.log(`[ssg-helmet] Prerenderer created successfully`);
|
|
43
|
+
break;
|
|
44
|
+
} catch (err) {
|
|
45
|
+
console.error(`[ssg-helmet] Prerenderer.create() failed on attempt ${attempt}:`, err.message);
|
|
46
|
+
|
|
47
|
+
// Clean up any partially-created prerenderer
|
|
48
|
+
if (prerenderer) {
|
|
49
|
+
try {
|
|
50
|
+
await prerenderer.close();
|
|
51
|
+
} catch (closeErr) {
|
|
52
|
+
console.error(`[ssg-helmet] Error during cleanup:`, closeErr.message);
|
|
53
|
+
}
|
|
54
|
+
prerenderer = null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (attempt === maxRetries) {
|
|
58
|
+
throw new Error(`Failed to create Prerenderer after ${maxRetries} attempts: ${err.message}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Exponential backoff: 2s, 4s
|
|
62
|
+
const delay = attempt * 2000;
|
|
63
|
+
console.log(`[ssg-helmet] Retrying in ${delay}ms...`);
|
|
64
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
36
67
|
|
|
37
68
|
while (queuedRoutes.length > 0) {
|
|
38
69
|
const knownRoutes = [...queuedRoutes, ...visistedRoutes];
|
|
@@ -63,7 +94,7 @@ async function main() {
|
|
|
63
94
|
|
|
64
95
|
const sitemapXml = buildSitemapXml(TARGET_DOMAIN, sitemapRoutes);
|
|
65
96
|
await fs.writeFile(
|
|
66
|
-
path.join(
|
|
97
|
+
path.join(REMIXER_BUILD_DIR, "sitemap.xml"),
|
|
67
98
|
sitemapXml,
|
|
68
99
|
"utf8",
|
|
69
100
|
);
|
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { UNPACK_SCRIPT_MARKER } from "./vars.mjs";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* that rewrites the URL back to the original SPA route BEFORE the app loads.
|
|
8
|
-
*
|
|
9
|
-
* - Idempotent: won't inject twice (uses a marker).
|
|
10
|
-
* - Injects before </head> if present, otherwise before </body>, otherwise appends.
|
|
11
|
-
*
|
|
12
|
-
* @param {string} outDir - your build output directory (e.g. "out")
|
|
13
|
-
*/
|
|
14
|
-
export async function injectUnpackRedirectScript(outDir) {
|
|
15
|
-
const indexPath = path.join(outDir, "index.html");
|
|
3
|
+
import { REMIXER_BUILD_DIR, UNPACK_SCRIPT_MARKER } from "./vars.mjs";
|
|
4
|
+
|
|
5
|
+
export async function injectUnpackRedirectScript() {
|
|
6
|
+
const indexPath = path.join(REMIXER_BUILD_DIR, "index.html");
|
|
16
7
|
let html = await fs.readFile(indexPath, "utf8");
|
|
17
8
|
|
|
18
9
|
if (html.includes(UNPACK_SCRIPT_MARKER)) return; // already injected
|
|
@@ -2,9 +2,12 @@ import http from "node:http";
|
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { URL } from "node:url";
|
|
5
|
+
import { setTimeout } from "node:timers/promises";
|
|
5
6
|
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
6
8
|
import * as chromeLauncher from "chrome-launcher";
|
|
7
9
|
import CDP from "chrome-remote-interface";
|
|
10
|
+
import { REMIXER_BUILD_DIR } from "./vars.mjs";
|
|
8
11
|
|
|
9
12
|
export class Prerenderer {
|
|
10
13
|
static async create() {
|
|
@@ -13,69 +16,75 @@ export class Prerenderer {
|
|
|
13
16
|
return instance;
|
|
14
17
|
}
|
|
15
18
|
|
|
19
|
+
#renderQueue = Promise.resolve();
|
|
20
|
+
|
|
16
21
|
async prerenderPage(route = "/") {
|
|
22
|
+
return (this.#renderQueue = this.#renderQueue.then(() =>
|
|
23
|
+
this.#renderPage(route),
|
|
24
|
+
));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async #renderPage(route) {
|
|
17
28
|
const r = this.#normalize(route);
|
|
29
|
+
const { Page, Runtime } = this.#client;
|
|
30
|
+
|
|
31
|
+
// Navigate
|
|
32
|
+
await Page.navigate({ url: `${this.#origin}${r}` });
|
|
33
|
+
await Page.loadEventFired();
|
|
34
|
+
|
|
35
|
+
// Wait for Helmet (non-fatal)
|
|
36
|
+
await Runtime.evaluate({
|
|
37
|
+
expression: `
|
|
38
|
+
(async function () {
|
|
39
|
+
const deadline = Date.now() + 15000;
|
|
40
|
+
while (Date.now() < deadline) {
|
|
41
|
+
if (document.head && document.head.querySelector('[data-react-helmet="true"]')) return true;
|
|
42
|
+
await new Promise(r => setTimeout(r, 50));
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
})()
|
|
46
|
+
`,
|
|
47
|
+
awaitPromise: true,
|
|
48
|
+
returnByValue: true,
|
|
49
|
+
});
|
|
18
50
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
51
|
+
await Runtime.evaluate({
|
|
52
|
+
expression: `new Promise(r => setTimeout(r, 100))`,
|
|
53
|
+
awaitPromise: true,
|
|
54
|
+
returnByValue: true,
|
|
55
|
+
});
|
|
22
56
|
|
|
23
|
-
|
|
57
|
+
// Snapshot
|
|
58
|
+
const { result } = await Runtime.evaluate({
|
|
59
|
+
expression: "document.documentElement.outerHTML",
|
|
60
|
+
returnByValue: true,
|
|
61
|
+
});
|
|
24
62
|
|
|
25
|
-
|
|
26
|
-
await Page.enable();
|
|
27
|
-
await Runtime.enable();
|
|
28
|
-
|
|
29
|
-
// Navigate
|
|
30
|
-
await Page.navigate({ url: `${this.#origin}${r}` });
|
|
31
|
-
await Page.loadEventFired(); // roughly equivalent to domcontentloaded+load
|
|
32
|
-
|
|
33
|
-
// Wait for Helmet (non-fatal)
|
|
34
|
-
await Runtime.evaluate({
|
|
35
|
-
expression: `
|
|
36
|
-
(async function () {
|
|
37
|
-
const deadline = Date.now() + 15000;
|
|
38
|
-
while (Date.now() < deadline) {
|
|
39
|
-
if (document.head && document.head.querySelector('[data-react-helmet="true"]')) return true;
|
|
40
|
-
await new Promise(r => setTimeout(r, 50));
|
|
41
|
-
}
|
|
42
|
-
return false;
|
|
43
|
-
})()
|
|
44
|
-
`,
|
|
45
|
-
awaitPromise: true,
|
|
46
|
-
returnByValue: true,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
// Small settle (like your 100ms)
|
|
50
|
-
await Runtime.evaluate({
|
|
51
|
-
expression: `new Promise(r => setTimeout(r, 100))`,
|
|
52
|
-
awaitPromise: true,
|
|
53
|
-
returnByValue: true,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// Snapshot
|
|
57
|
-
const { result } = await Runtime.evaluate({
|
|
58
|
-
expression: "document.documentElement.outerHTML",
|
|
59
|
-
returnByValue: true,
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
return result.value;
|
|
63
|
-
} finally {
|
|
64
|
-
await client.close().catch(() => {});
|
|
65
|
-
await CDP.Close({ port: this.#cdpPort, id: target.id }).catch(() => {});
|
|
66
|
-
}
|
|
63
|
+
return result.value;
|
|
67
64
|
}
|
|
68
65
|
|
|
69
66
|
async close() {
|
|
70
|
-
|
|
67
|
+
if (this.#client) {
|
|
68
|
+
await this.#client.close().catch(() => {});
|
|
69
|
+
this.#client = null;
|
|
70
|
+
}
|
|
71
|
+
|
|
71
72
|
if (this.#chrome) {
|
|
72
73
|
try {
|
|
73
|
-
|
|
74
|
+
if (typeof this.#chrome.kill === "function") {
|
|
75
|
+
await this.#chrome.kill();
|
|
76
|
+
}
|
|
74
77
|
} catch {}
|
|
75
78
|
this.#chrome = null;
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
|
|
81
|
+
if (this.#chromeProcess) {
|
|
82
|
+
try {
|
|
83
|
+
this.#chromeProcess.kill("SIGKILL");
|
|
84
|
+
} catch {}
|
|
85
|
+
this.#chromeProcess = null;
|
|
86
|
+
}
|
|
87
|
+
|
|
79
88
|
if (this.#server) {
|
|
80
89
|
await new Promise((res) => this.#server.close(res));
|
|
81
90
|
this.#server = null;
|
|
@@ -85,8 +94,31 @@ export class Prerenderer {
|
|
|
85
94
|
// ================= PRIVATE =================
|
|
86
95
|
|
|
87
96
|
constructor() {
|
|
88
|
-
this.#
|
|
89
|
-
|
|
97
|
+
this.#indexPath = path.join(REMIXER_BUILD_DIR, "index.html");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async #verifyCDPReady() {
|
|
101
|
+
const maxAttempts = 20;
|
|
102
|
+
const delayMs = 500;
|
|
103
|
+
|
|
104
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
105
|
+
try {
|
|
106
|
+
await CDP.List({ port: this.#cdpPort });
|
|
107
|
+
if (attempt > 1) {
|
|
108
|
+
console.log(`[ssg-helmet] CDP ready after ${attempt} attempts`);
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.log(`[ssg-helmet] CDP verification attempt ${attempt}/${maxAttempts} failed: ${error.message}`);
|
|
113
|
+
if (attempt === maxAttempts) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Chrome CDP not ready after ${maxAttempts * delayMs}ms. ` +
|
|
116
|
+
`Chrome may have crashed. Error: ${error.message}`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
await setTimeout(delayMs);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
90
122
|
}
|
|
91
123
|
|
|
92
124
|
async #init() {
|
|
@@ -94,8 +126,97 @@ export class Prerenderer {
|
|
|
94
126
|
|
|
95
127
|
await this.#startServer();
|
|
96
128
|
|
|
129
|
+
// In Lambda (CHROME_BIN set), spawn Chrome directly to capture stderr.
|
|
130
|
+
// Locally, use chrome-launcher for convenience.
|
|
131
|
+
if (process.env.CHROME_BIN) {
|
|
132
|
+
await this.#launchDirect();
|
|
133
|
+
} else {
|
|
134
|
+
await this.#launchWithChromeLauncher();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
console.log(`[ssg-helmet] Chrome process launched on port ${this.#cdpPort}, verifying CDP readiness...`);
|
|
138
|
+
await this.#verifyCDPReady();
|
|
139
|
+
console.log(`[ssg-helmet] Chrome CDP ready`);
|
|
140
|
+
|
|
141
|
+
// Connect to the default tab
|
|
142
|
+
const targets = await CDP.List({ port: this.#cdpPort });
|
|
143
|
+
const pageTarget = targets.find((t) => t.type === "page");
|
|
144
|
+
if (!pageTarget) throw new Error("No default page target found in Chrome");
|
|
145
|
+
|
|
146
|
+
this.#client = await CDP({ port: this.#cdpPort, target: pageTarget });
|
|
147
|
+
await this.#client.Page.enable();
|
|
148
|
+
await this.#client.Runtime.enable();
|
|
149
|
+
console.log(`[ssg-helmet] Connected to default tab`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async #launchDirect() {
|
|
153
|
+
const chromeBin = process.env.CHROME_BIN;
|
|
154
|
+
const debugPort = 9222 + Math.floor(Math.random() * 10000);
|
|
155
|
+
|
|
156
|
+
// Log Chrome version first
|
|
157
|
+
try {
|
|
158
|
+
const ver = spawn(chromeBin, ["--version"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
159
|
+
let out = "";
|
|
160
|
+
ver.stdout.on("data", (d) => (out += d));
|
|
161
|
+
await new Promise((res) => ver.on("close", res));
|
|
162
|
+
console.log(`[ssg-helmet] Chrome version: ${out.trim()}`);
|
|
163
|
+
} catch {}
|
|
164
|
+
|
|
165
|
+
const defaultFlags = [
|
|
166
|
+
"--headless",
|
|
167
|
+
"--no-sandbox",
|
|
168
|
+
"--disable-setuid-sandbox",
|
|
169
|
+
"--disable-gpu",
|
|
170
|
+
"--disable-dev-shm-usage",
|
|
171
|
+
"--no-zygote",
|
|
172
|
+
"--disable-background-networking",
|
|
173
|
+
"--disable-extensions",
|
|
174
|
+
"--disable-background-timer-throttling",
|
|
175
|
+
"--disable-backgrounding-occluded-windows",
|
|
176
|
+
"--disable-renderer-backgrounding",
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
const chromeFlags = process.env.CHROME_FLAGS
|
|
180
|
+
? ["--headless=new", ...process.env.CHROME_FLAGS.split(",").map((f) => f.trim())]
|
|
181
|
+
: defaultFlags;
|
|
182
|
+
|
|
183
|
+
const allArgs = [
|
|
184
|
+
...chromeFlags,
|
|
185
|
+
"--user-data-dir=/tmp/chrome-data",
|
|
186
|
+
`--remote-debugging-port=${debugPort}`,
|
|
187
|
+
"--remote-debugging-address=127.0.0.1",
|
|
188
|
+
"about:blank",
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
console.log(`[ssg-helmet] Spawning ${chromeBin} on port ${debugPort}`);
|
|
192
|
+
console.log(`[ssg-helmet] Args: ${allArgs.join(" ")}`);
|
|
193
|
+
|
|
194
|
+
this.#chromeProcess = spawn(chromeBin, allArgs, {
|
|
195
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
196
|
+
env: {
|
|
197
|
+
...process.env,
|
|
198
|
+
HOME: "/tmp",
|
|
199
|
+
XDG_CACHE_HOME: "/tmp/.cache",
|
|
200
|
+
XDG_CONFIG_HOME: "/tmp/.config",
|
|
201
|
+
FONTCONFIG_CACHE: "/tmp/fontconfig-cache",
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
let stderrBuf = "";
|
|
206
|
+
this.#chromeProcess.stderr.on("data", (d) => {
|
|
207
|
+
stderrBuf += d.toString();
|
|
208
|
+
});
|
|
209
|
+
this.#chromeProcess.on("exit", (code, signal) => {
|
|
210
|
+
console.log(`[ssg-helmet] Chrome exited: code=${code}, signal=${signal}`);
|
|
211
|
+
if (stderrBuf) console.log(`[ssg-helmet] Chrome stderr:\n${stderrBuf.slice(0, 4000)}`);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
this.#cdpPort = debugPort;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async #launchWithChromeLauncher() {
|
|
97
218
|
const defaultFlags = [
|
|
98
|
-
"--headless
|
|
219
|
+
"--headless",
|
|
99
220
|
"--no-sandbox",
|
|
100
221
|
"--disable-gpu",
|
|
101
222
|
"--disable-dev-shm-usage",
|
|
@@ -104,9 +225,11 @@ export class Prerenderer {
|
|
|
104
225
|
];
|
|
105
226
|
|
|
106
227
|
const chromeFlags = process.env.CHROME_FLAGS
|
|
107
|
-
? ["--headless
|
|
228
|
+
? ["--headless", ...process.env.CHROME_FLAGS.split(",").map((f) => f.trim())]
|
|
108
229
|
: defaultFlags;
|
|
109
230
|
|
|
231
|
+
console.log(`[ssg-helmet] Launching Chrome with flags:`, chromeFlags);
|
|
232
|
+
|
|
110
233
|
this.#chrome = await chromeLauncher.launch({
|
|
111
234
|
chromePath: process.env.CHROME_BIN || undefined,
|
|
112
235
|
chromeFlags,
|
|
@@ -133,7 +256,7 @@ export class Prerenderer {
|
|
|
133
256
|
return res.end("Bad Request");
|
|
134
257
|
}
|
|
135
258
|
|
|
136
|
-
const filePath = path.join(
|
|
259
|
+
const filePath = path.join(REMIXER_BUILD_DIR, safe);
|
|
137
260
|
|
|
138
261
|
try {
|
|
139
262
|
const stat = await fs.stat(filePath);
|
|
@@ -183,9 +306,10 @@ export class Prerenderer {
|
|
|
183
306
|
|
|
184
307
|
#server;
|
|
185
308
|
#origin;
|
|
186
|
-
#outDir;
|
|
187
309
|
#indexPath;
|
|
188
310
|
|
|
311
|
+
#client;
|
|
189
312
|
#chrome;
|
|
313
|
+
#chromeProcess;
|
|
190
314
|
#cdpPort;
|
|
191
315
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export const
|
|
4
|
-
|
|
5
|
-
export const
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
export const REMIXER_BUILD_DIR =
|
|
4
|
+
process.env.REMIXER_BUILD_DIR || path.resolve("./out");
|
|
5
|
+
export const REMIXER_SOURCE_DIR =
|
|
6
|
+
process.env.REMIXER_SOURCE_DIR || path.resolve("./");
|
|
6
7
|
export const TARGET_DOMAIN =
|
|
7
8
|
process.env.NEXT_PUBLIC_DOMAIN || "https://example.com";
|
|
9
|
+
|
|
10
|
+
export const ROOT_ROUTE = "/";
|
|
11
|
+
export const IS_INDEXABLE = true;
|
|
12
|
+
export const IS_NOT_INDEXABLE = false;
|
|
8
13
|
export const UNPACK_SCRIPT_MARKER = "redirect-helmet-unpack-p-v1";
|
|
@@ -26,12 +26,6 @@
|
|
|
26
26
|
"@types/node": "22.19.1",
|
|
27
27
|
"@vitejs/plugin-react": "5.1.1",
|
|
28
28
|
"typescript": "5.8.3",
|
|
29
|
-
"vite": "6.4.1"
|
|
30
|
-
"chrome-launcher": "1.2.1",
|
|
31
|
-
"chrome-remote-interface": "0.34.0",
|
|
32
|
-
"acorn": "^8.16.0",
|
|
33
|
-
"acorn-jsx": "^5.3.2",
|
|
34
|
-
"sucrase": "^3.35.1",
|
|
35
|
-
"estree-walker": "^3.0.3"
|
|
29
|
+
"vite": "6.4.1"
|
|
36
30
|
}
|
|
37
31
|
}
|