@mdn/fred 1.1.1 → 1.2.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/CHANGELOG.md +12 -0
- package/README.md +4 -0
- package/build/env.js +11 -2
- package/build/render.js +4 -8
- package/build/ssr.js +2 -2
- package/components/env/README.md +28 -0
- package/components/env/index.js +2 -29
- package/components/env/runtime.js +6 -0
- package/components/env/types.d.ts +5 -0
- package/components/env/utils.js +45 -0
- package/components/outer-layout/server.js +12 -0
- package/out/service-worker.js +1 -1
- package/out/static/client/{1231.df3f6728f4b7e36e.js → 1231.84c230e0fa92f2d4.js} +2 -2
- package/out/static/client/{1231.df3f6728f4b7e36e.js.map → 1231.84c230e0fa92f2d4.js.map} +1 -1
- package/out/static/client/{1274.5c4c7493aa8334bf.js → 1274.e98f5c68889cda35.js} +2 -2
- package/out/static/client/{1274.5c4c7493aa8334bf.js.map → 1274.e98f5c68889cda35.js.map} +1 -1
- package/out/static/client/{2936.2446688864e1a136.js → 2936.7d85bf584b89578b.js} +2 -2
- package/out/static/client/{2936.2446688864e1a136.js.map → 2936.7d85bf584b89578b.js.map} +1 -1
- package/out/static/client/{370.85e6b0ff8596c5ae.js → 370.d2e13711140af6d1.js} +3 -3
- package/out/static/client/{370.85e6b0ff8596c5ae.js.map → 370.d2e13711140af6d1.js.map} +1 -1
- package/out/static/client/{451.d36f6f80badd6af6.js → 451.cd85e2808f388a00.js} +2 -2
- package/out/static/client/{451.d36f6f80badd6af6.js.map → 451.cd85e2808f388a00.js.map} +1 -1
- package/out/static/client/{606.221fc4347a8278e8.js → 606.d22d2cf33232a730.js} +2 -2
- package/out/static/client/{606.221fc4347a8278e8.js.map → 606.d22d2cf33232a730.js.map} +1 -1
- package/out/static/client/{8093.81b1b345d454a85e.js → 8093.b03f1cb1e8b0984a.js} +3 -3
- package/out/static/client/{8093.81b1b345d454a85e.js.map → 8093.b03f1cb1e8b0984a.js.map} +1 -1
- package/out/static/client/{9784.0096d3445a2a7e98.js → 9784.214845ec6d3b77e3.js} +3 -3
- package/out/static/client/{9784.0096d3445a2a7e98.js.map → 9784.214845ec6d3b77e3.js.map} +1 -1
- package/out/static/client/{9837.2374d8bd425a9866.js → 9837.6876cb5a9d3ad763.js} +2 -2
- package/out/static/client/{9837.2374d8bd425a9866.js.map → 9837.6876cb5a9d3ad763.js.map} +1 -1
- package/out/static/client/{9914.9b7171874efa4629.js → 9914.d49932c28ec56667.js} +2 -2
- package/out/static/client/{9914.9b7171874efa4629.js.map → 9914.d49932c28ec56667.js.map} +1 -1
- package/out/static/client/{index.dfccf913556b909a.js → index.0910bee955d71d1b.js} +5 -5
- package/out/static/client/index.0910bee955d71d1b.js.map +1 -0
- package/out/static/client/{runtime.05472639a6b3fea7.js → runtime.3216007ef48b58b9.js} +2 -2
- package/out/static/client/{runtime.05472639a6b3fea7.js.map → runtime.3216007ef48b58b9.js.map} +1 -1
- package/out/static/client/stats.json +112 -112
- package/out/static/client/{styles-global.1edda3f0378c4985.js → styles-global.5e83bb0f2c4c0268.js} +1 -1
- package/out/static/client/styles-global.a9d314985c899197.css +2 -0
- package/out/static/client/{styles-global.c9f2a49e47bf5c55.css.map → styles-global.a9d314985c899197.css.map} +1 -1
- package/out/static/legacy/asset-manifest.json +5 -5
- package/out/static/legacy/{index.c10af7d92675e9fd.html → index.bde4adba6f0cf3ac.html} +1 -1
- package/out/static/legacy/{index.4b9e35f28a890bda.js → index.e03b3082693746b6.js} +3 -3
- package/out/static/legacy/{index.4b9e35f28a890bda.js.map → index.e03b3082693746b6.js.map} +1 -1
- package/out/static/legacy/stats.json +10 -10
- package/out/static/legacy/{yari.2771217736dd92b4.js → yari.bd680acb6063d7c4.js} +3 -3
- package/out/static/legacy/{yari.2771217736dd92b4.js.map → yari.bd680acb6063d7c4.js.map} +1 -1
- package/out/static/ssr/index.js +10 -7
- package/out/static/ssr/index.js.map +1 -1
- package/out/static/ssr/stats.json +4 -4
- package/package.json +1 -1
- package/rspack.config.js +8 -7
- package/server.js +3 -3
- package/out/static/client/index.dfccf913556b909a.js.map +0 -1
- package/out/static/client/styles-global.c9f2a49e47bf5c55.css +0 -2
- /package/out/static/client/{370.85e6b0ff8596c5ae.js.LICENSE.txt → 370.d2e13711140af6d1.js.LICENSE.txt} +0 -0
- /package/out/static/client/{8093.81b1b345d454a85e.js.LICENSE.txt → 8093.b03f1cb1e8b0984a.js.LICENSE.txt} +0 -0
- /package/out/static/client/{9784.0096d3445a2a7e98.js.LICENSE.txt → 9784.214845ec6d3b77e3.js.LICENSE.txt} +0 -0
- /package/out/static/client/{index.dfccf913556b909a.js.LICENSE.txt → index.0910bee955d71d1b.js.LICENSE.txt} +0 -0
- /package/out/static/legacy/{index.4b9e35f28a890bda.js.LICENSE.txt → index.e03b3082693746b6.js.LICENSE.txt} +0 -0
- /package/out/static/legacy/{yari.2771217736dd92b4.js.LICENSE.txt → yari.bd680acb6063d7c4.js.LICENSE.txt} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.0](https://github.com/mdn/fred/compare/v1.1.1...v1.2.0) (2025-08-28)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **env:** add runtime variables ([#642](https://github.com/mdn/fred/issues/642)) ([ab504ef](https://github.com/mdn/fred/commit/ab504effc762002df03d709e9ed605f58b6b2b22))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **npm:** js/styles/assets weren't being found ([#641](https://github.com/mdn/fred/issues/641)) ([1c4e02a](https://github.com/mdn/fred/commit/1c4e02a2ea20da72e8a43b9362c8a285f060af5c))
|
|
14
|
+
|
|
3
15
|
## [1.1.1](https://github.com/mdn/fred/compare/v1.1.0...v1.1.1) (2025-08-28)
|
|
4
16
|
|
|
5
17
|
|
package/README.md
CHANGED
|
@@ -26,6 +26,10 @@ MDN's next fr(ont)e(n)d.
|
|
|
26
26
|
|
|
27
27
|
## Development principles
|
|
28
28
|
|
|
29
|
+
### Environment variables
|
|
30
|
+
|
|
31
|
+
See [the environment variables README](./components/env/README.md).
|
|
32
|
+
|
|
29
33
|
### Inline JS
|
|
30
34
|
|
|
31
35
|
We need to run some JS as soon as possible at page load, to avoid layout shifts and flashes.
|
package/build/env.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
const defaultOut = path.join(import.meta.dirname, "..", "out");
|
|
4
|
+
const nodeModule = defaultOut.endsWith(
|
|
5
|
+
path.join("node_modules", "@mdn", "fred", "out"),
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
export const RARI_BUILD_ROOT = process.env.BUILD_OUT_ROOT || defaultOut;
|
|
9
|
+
|
|
10
|
+
// When running from an npm package, this needs to be the default output directory,
|
|
11
|
+
// so we can load the pre-built assets
|
|
12
|
+
export const FRED_BUILD_ROOT = nodeModule ? defaultOut : RARI_BUILD_ROOT;
|
package/build/render.js
CHANGED
|
@@ -2,27 +2,23 @@ import { readFile } from "node:fs/promises";
|
|
|
2
2
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
// but when we package it into an npm package, it needs to be a relative path
|
|
7
|
-
// based on the value of BUILD_OUT_ROOT at that point in time
|
|
8
|
-
// https://github.com/mdn/fred/issues/594
|
|
9
|
-
const BUILD_OUT_ROOT = path.resolve(import.meta.dirname, "..", "out");
|
|
5
|
+
import { FRED_BUILD_ROOT } from "./env.js";
|
|
10
6
|
|
|
11
7
|
const { render: distRender } = /** @type {import("../entry.ssr.js")} */ (
|
|
12
|
-
await import(path.resolve(
|
|
8
|
+
await import(path.resolve(FRED_BUILD_ROOT, "static", "ssr", "index.js"))
|
|
13
9
|
);
|
|
14
10
|
|
|
15
11
|
/** @type {import("@rspack/core").StatsCompilation} */
|
|
16
12
|
const clientManifest = JSON.parse(
|
|
17
13
|
await readFile(
|
|
18
|
-
path.join(
|
|
14
|
+
path.join(FRED_BUILD_ROOT, "static", "client", "stats.json"),
|
|
19
15
|
"utf8",
|
|
20
16
|
),
|
|
21
17
|
);
|
|
22
18
|
/** @type {import("@rspack/core").StatsCompilation} */
|
|
23
19
|
const legacyManifest = JSON.parse(
|
|
24
20
|
await readFile(
|
|
25
|
-
path.join(
|
|
21
|
+
path.join(FRED_BUILD_ROOT, "static", "legacy", "stats.json"),
|
|
26
22
|
"utf8",
|
|
27
23
|
),
|
|
28
24
|
);
|
package/build/ssr.js
CHANGED
|
@@ -2,7 +2,7 @@ import { readFile, writeFile } from "node:fs/promises";
|
|
|
2
2
|
|
|
3
3
|
import { fdir } from "fdir";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { RARI_BUILD_ROOT } from "./env.js";
|
|
6
6
|
import { render } from "./render.js";
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -59,7 +59,7 @@ async function findDocuments() {
|
|
|
59
59
|
.withFullPaths()
|
|
60
60
|
.withErrors()
|
|
61
61
|
.filter((filePath) => filePath.endsWith("/index.json"))
|
|
62
|
-
.crawl(
|
|
62
|
+
.crawl(RARI_BUILD_ROOT);
|
|
63
63
|
const docs = await api.withPromise();
|
|
64
64
|
return docs;
|
|
65
65
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Environment Variables
|
|
2
|
+
|
|
3
|
+
If a component or other part of Fred requires a variable set from the environment, you should define it in the [`./index.js`](./index.js) file, with a safe default value.
|
|
4
|
+
|
|
5
|
+
## Defaults
|
|
6
|
+
|
|
7
|
+
We set safe defaults for prod, unless the risk from doing - and having this set everywhere, across local dev etc. - outweighs the risk of it not being set on prod.
|
|
8
|
+
|
|
9
|
+
E.g. we don't set `GLEAN_ENABLED` to the prod default, as we don't want to send telemetry pings for prod from other environments, and we'll notice in our telemetry if it's not set in prod.
|
|
10
|
+
|
|
11
|
+
## Naming
|
|
12
|
+
|
|
13
|
+
Within Fred, variables are unprefixed, however the environment variable name is prefixed with `FRED_` to avoid conflicts with other environment variables. For example, if you define a variable `MY_VAR` in Fred, the environment variable to set would be `FRED_MY_VAR`.
|
|
14
|
+
|
|
15
|
+
## Secrets
|
|
16
|
+
|
|
17
|
+
Rspack will bundle **all** environment variables prefixed with `FRED_` into the bundle, which is exposed client side.
|
|
18
|
+
|
|
19
|
+
> [!WARNING]
|
|
20
|
+
> **Never set secrets** through `FRED_` environment variables.
|
|
21
|
+
>
|
|
22
|
+
> As secrets can only be used in server-side components, you should simply set them through a non-`FRED_`-prefixed environment variable, and access them using the `process.env` object.
|
|
23
|
+
|
|
24
|
+
## Build-time vs runtime
|
|
25
|
+
|
|
26
|
+
By default, variables are baked into our bundle at build time. However, you can define variables as runtime environment variables, which can be changed when running a version of Fred built with `FRED_RUNTIME_ENV=true`.
|
|
27
|
+
|
|
28
|
+
This is used in our npm package, so we can e.g. set `FRED_WRITER_MODE=true` in the `content` repo, without having to bake that into the npm package for all consumers.
|
package/components/env/index.js
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* @file Retrieves environment variables, setting defaults, for other areas of the app.
|
|
3
|
-
*
|
|
4
|
-
* We set safe defaults for prod, unless the risk from doing - and having this set
|
|
5
|
-
* everywhere, across local dev etc. - outweighs the risk of it not being set on prod.
|
|
6
|
-
*/
|
|
1
|
+
import { parseBool, parseString } from "./utils.js";
|
|
7
2
|
|
|
8
3
|
export const PLAYGROUND_BASE_HOST = parseString(
|
|
9
4
|
"PLAYGROUND_BASE_HOST",
|
|
@@ -33,7 +28,7 @@ export const GLEAN_DEBUG = parseBool("GLEAN_DEBUG", false);
|
|
|
33
28
|
*/
|
|
34
29
|
export const ROBOTS_GLOBAL_ALLOW = parseBool("ROBOTS_GLOBAL_ALLOW", true);
|
|
35
30
|
|
|
36
|
-
export const WRITER_MODE = parseBool("WRITER_MODE", false);
|
|
31
|
+
export const WRITER_MODE = parseBool("WRITER_MODE", false, { runtime: true });
|
|
37
32
|
|
|
38
33
|
export const BCD_BASE_URL = parseString(
|
|
39
34
|
"BCD_BASE_URL",
|
|
@@ -44,25 +39,3 @@ export const OBSERVATORY_API_URL = parseString(
|
|
|
44
39
|
"OBSERVATORY_API_URL",
|
|
45
40
|
"https://observatory-api.mdn.mozilla.net",
|
|
46
41
|
);
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* @param {string} name
|
|
50
|
-
* @param {boolean} fallback
|
|
51
|
-
*/
|
|
52
|
-
function parseBool(name, fallback) {
|
|
53
|
-
try {
|
|
54
|
-
return Boolean(
|
|
55
|
-
JSON.parse(process.env[`FRED_${name}`] || JSON.stringify(fallback)),
|
|
56
|
-
);
|
|
57
|
-
} catch {
|
|
58
|
-
return fallback;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* @param {string} name
|
|
64
|
-
* @param {string} fallback
|
|
65
|
-
*/
|
|
66
|
-
function parseString(name, fallback) {
|
|
67
|
-
return process.env[`FRED_${name}`] || fallback;
|
|
68
|
-
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { parseBool } from "./utils.js";
|
|
2
|
+
|
|
3
|
+
/** @type {string[]} */
|
|
4
|
+
export const runtimeVariables = [];
|
|
5
|
+
/** Overriden to prod default (false) in rspack config, set to true so it works in dev server by default. */
|
|
6
|
+
export const RUNTIME_ENV = parseBool("RUNTIME_ENV", true);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { RUNTIME_ENV, runtimeVariables } from "./runtime.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {object} Options
|
|
5
|
+
* @property {boolean} [runtime] Allow setting this value at runtime
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {string} name
|
|
10
|
+
* @param {boolean} fallback
|
|
11
|
+
* @param {Options} [options]
|
|
12
|
+
*/
|
|
13
|
+
export function parseBool(name, fallback, options) {
|
|
14
|
+
try {
|
|
15
|
+
return Boolean(
|
|
16
|
+
JSON.parse(getEnv(name, options) || JSON.stringify(fallback)),
|
|
17
|
+
);
|
|
18
|
+
} catch {
|
|
19
|
+
return fallback;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {string} name
|
|
25
|
+
* @param {string} fallback
|
|
26
|
+
* @param {Options} [options]
|
|
27
|
+
*/
|
|
28
|
+
export function parseString(name, fallback, options) {
|
|
29
|
+
return getEnv(name, options) || fallback;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {string} name
|
|
34
|
+
* @param {Options} [options]
|
|
35
|
+
* @returns {string | undefined}
|
|
36
|
+
*/
|
|
37
|
+
function getEnv(name, options = {}) {
|
|
38
|
+
const { runtime } = { runtime: false, ...options };
|
|
39
|
+
name = `FRED_${name}`;
|
|
40
|
+
if (runtime && RUNTIME_ENV) {
|
|
41
|
+
runtimeVariables.push(name);
|
|
42
|
+
return process.env[name] || getEnv(name);
|
|
43
|
+
}
|
|
44
|
+
return globalThis.__MDNEnv?.[name];
|
|
45
|
+
}
|
|
@@ -5,6 +5,7 @@ import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
|
5
5
|
|
|
6
6
|
import inlineScript from "../../entry.inline.js?source&csp=true";
|
|
7
7
|
import { ROBOTS_GLOBAL_ALLOW, WRITER_MODE } from "../env/index.js";
|
|
8
|
+
import { RUNTIME_ENV, runtimeVariables } from "../env/runtime.js";
|
|
8
9
|
import Favicon from "../favicon/pure.js";
|
|
9
10
|
import { asyncLocalStorage } from "../server/async-local-storage.js";
|
|
10
11
|
import { ServerComponent } from "../server/index.js";
|
|
@@ -68,6 +69,12 @@ export class OuterLayout extends ServerComponent {
|
|
|
68
69
|
? "learn"
|
|
69
70
|
: undefined;
|
|
70
71
|
|
|
72
|
+
const env = Object.fromEntries(
|
|
73
|
+
Object.entries(process.env).filter(([key]) =>
|
|
74
|
+
runtimeVariables.includes(key),
|
|
75
|
+
),
|
|
76
|
+
);
|
|
77
|
+
|
|
71
78
|
// if you want to put some script inline, put it in entry.inline.js
|
|
72
79
|
// and you'll get CSP generation: see the README
|
|
73
80
|
return html`
|
|
@@ -86,6 +93,11 @@ export class OuterLayout extends ServerComponent {
|
|
|
86
93
|
content="width=device-width, initial-scale=1.0"
|
|
87
94
|
/>
|
|
88
95
|
<title>${context.pageTitle || "MDN"}</title>
|
|
96
|
+
${RUNTIME_ENV
|
|
97
|
+
? unsafeHTML(`<script>process = {
|
|
98
|
+
env: ${JSON.stringify(env)}
|
|
99
|
+
};</script>`)
|
|
100
|
+
: nothing}
|
|
89
101
|
${unsafeHTML(`<script>${inlineScript}</script>`)}
|
|
90
102
|
${styles.map(
|
|
91
103
|
(path) =>
|