Skip to content

Commit 913c43a

Browse files
committed
Add react-dsfr index CLI
1 parent a02af5a commit 913c43a

File tree

8 files changed

+88
-57
lines changed

8 files changed

+88
-57
lines changed

.storybook/manager-head.html

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
<link rel="apple-touch-icon" href="%PUBLIC_URL%/dsfr/favicon/apple-touch-icon.png" />
2-
<link rel="icon" href="%PUBLIC_URL%/dsfr/favicon/favicon.svg" type="image/svg+xml" />
3-
<link rel="shortcut icon" href="%PUBLIC_URL%/dsfr/favicon/favicon.ico" type="image/x-icon" />
4-
<link rel="manifest" href="%PUBLIC_URL%/dsfr/favicon/manifest.webmanifest" crossorigin="use-credentials" />
5-
<link rel="stylesheet" href="%PUBLIC_URL%/dsfr/fonts/index.css" />
1+
<link rel="apple-touch-icon" href="/dsfr/favicon/apple-touch-icon.png" />
2+
<link rel="icon" href="/dsfr/favicon/favicon.svg" type="image/svg+xml" />
3+
<link rel="shortcut icon" href="/dsfr/favicon/favicon.ico" type="image/x-icon" />
4+
<link rel="manifest" href="/dsfr/favicon/manifest.webmanifest" crossorigin="use-credentials" />
5+
<link rel="stylesheet" href="/dsfr/fonts/index.css" />
66

77
<!-- Meta tags generated by metatags.io -->
88
<!-- Primary Meta Tags -->
@@ -16,13 +16,13 @@
1616
<meta property="og:type" content="website">
1717
<meta property="og:title" content="react-dsfr components">
1818
<meta property="og:description" content="@codegouvfr/react-dsfr components playground and showcase">
19-
<meta property="og:image" content="%PUBLIC_URL%/repo-card.png">
19+
<meta property="og:image" content="/repo-card.png">
2020

2121
<!-- Twitter Meta Tags -->
2222
<meta name="twitter:card" content="summary_large_image">
2323
<meta name="twitter:title" content="react-dsfr components">
2424
<meta name="twitter:description" content="@codegouvfr/react-dsfr components playground and showcase">
25-
<meta name="twitter:image" content="%PUBLIC_URL%/repo-card.png">
25+
<meta name="twitter:image" content="/repo-card.png">
2626

2727
<style>
2828
.sidebar-item[data-nodetype="document"] svg {

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"prebuild-storybook": "yarn prestorybook"
2727
},
2828
"bin": {
29+
"react-dsfr": "dist/bin/react-dsfr.js",
2930
"copy-dsfr-to-public": "dist/bin/copy-dsfr-to-public.js",
3031
"only-include-used-icons": "dist/bin/only-include-used-icons.js"
3132
},

scripts/build/build.ts

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -133,30 +133,30 @@ import yargsParser from "yargs-parser";
133133
break local_testing;
134134
}
135135

136-
fs.writeFileSync(
137-
pathJoin(distDirPath, "package.json"),
138-
Buffer.from(
139-
JSON.stringify(
140-
(() => {
141-
const packageJsonParsed = JSON.parse(
142-
fs
143-
.readFileSync(pathJoin(projectRootDirPath, "package.json"))
144-
.toString("utf8")
145-
);
146-
147-
return {
148-
...packageJsonParsed,
149-
"main": packageJsonParsed["main"].replace(/^dist\//, ""),
150-
"types": packageJsonParsed["types"].replace(/^dist\//, ""),
151-
"module": packageJsonParsed["module"].replace(/^dist\//, "")
152-
};
153-
})(),
154-
null,
155-
2
156-
),
157-
"utf8"
158-
)
159-
);
136+
{
137+
let modifiedPackageJsonContent = fs
138+
.readFileSync(pathJoin(projectRootDirPath, "package.json"))
139+
.toString("utf8");
140+
141+
modifiedPackageJsonContent = (() => {
142+
const o = JSON.parse(modifiedPackageJsonContent);
143+
144+
delete o.files;
145+
146+
return JSON.stringify(o, null, 2);
147+
})();
148+
149+
modifiedPackageJsonContent = modifiedPackageJsonContent
150+
.replace(/"dist\//g, '"')
151+
.replace(/"\.\/dist\//g, '"./')
152+
.replace(/"!dist\//g, '"!')
153+
.replace(/"!\.\/dist\//g, '"!./');
154+
155+
fs.writeFileSync(
156+
pathJoin(distDirPath, "package.json"),
157+
Buffer.from(modifiedPackageJsonContent, "utf8")
158+
);
159+
}
160160

161161
fs.cpSync(dsfrDirPath, pathJoin(distDirPath, "dsfr"), { "recursive": true });
162162
fs.rmSync(dsfrDirPath, { "recursive": true });

scripts/build/icons.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { parseCss } from "./parseCss";
66
import { assert } from "tsafe/assert";
77
import { exclude } from "tsafe/exclude";
88
import type { Icon } from "../../src/bin/only-include-used-icons";
9-
import { pathOfPatchedRawCssCodeForCompatWithRemixIconRelativeToDsfrDist } from "../../src/bin/only-include-used-icons";
9+
import { PATH_OF_PATCHED_RAW_CSS_CODE_FOR_COMPAT_WITH_REMIXICON_RELATIVE_TO_DSFR } from "../../src/bin/only-include-used-icons";
1010
import { sep } from "path";
1111
import * as css from "css";
1212

@@ -55,7 +55,8 @@ export function getPatchedRawCssCodeForCompatWithRemixIcon(params: { rawCssCode:
5555

5656
const back =
5757
new Array(
58-
pathOfPatchedRawCssCodeForCompatWithRemixIconRelativeToDsfrDist.split(sep).length - 1
58+
PATH_OF_PATCHED_RAW_CSS_CODE_FOR_COMPAT_WITH_REMIXICON_RELATIVE_TO_DSFR.split(sep)
59+
.length - 1
5960
)
6061
.fill("..")
6162
.join("/") + "/";

scripts/link-in-external-project.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ if (testAppPaths.length === 0) {
7070
process.exit(-1);
7171
}
7272

73-
testAppPaths.forEach(testAppPath => execSync("yarn install", { "cwd": testAppPath }));
73+
testAppPaths.forEach(testAppPath =>
74+
execSync("yarn install --ignore-scripts", { "cwd": testAppPath })
75+
);
7476

7577
console.log("=== Linking common dependencies ===");
7678

src/bin/copy-dsfr-to-public.ts

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#!/usr/bin/env node
22

33
/**
4-
* This script is ran with `npx copy-dsfr-to-public`
4+
* This script is ran with `npx react-dsfr copy-dsfr-to-public`
55
* It takes one optional arguments (for NX monorepos):
66
* - `--projectDir <path>` to specify the project directory. Default to the current working directory.
77
* This can be used in monorepos to specify the react project directory.
88
*/
99

10-
import { join as pathJoin, resolve as pathResolve, relative as pathRelative } from "path";
10+
import { join as pathJoin, resolve as pathResolve } from "path";
1111
import * as fs from "fs";
1212
import { getProjectRoot } from "./tools/getProjectRoot";
1313
import yargsParser from "yargs-parser";
@@ -17,8 +17,8 @@ import { transformCodebase } from "./tools/transformCodebase";
1717
import { assert } from "tsafe/assert";
1818
import { modifyHtmlHrefs } from "./tools/modifyHtmlHrefs";
1919

20-
(async () => {
21-
const argv = yargsParser(process.argv.slice(2));
20+
export async function main(args: string[]) {
21+
const argv = yargsParser(args);
2222

2323
const projectDirPath: string = (() => {
2424
read_from_argv: {
@@ -69,7 +69,7 @@ import { modifyHtmlHrefs } from "./tools/modifyHtmlHrefs";
6969

7070
const gouvFrDsfrVersion: string = JSON.parse(
7171
fs.readFileSync(pathJoin(getProjectRoot(), "package.json")).toString("utf8")
72-
)["dependencies"]["@gouvfr/dsfr"];
72+
)["devDependencies"]["@gouvfr/dsfr"];
7373

7474
const versionFilePath = pathJoin(dsfrDirPath, "version.txt");
7575

@@ -89,10 +89,6 @@ import { modifyHtmlHrefs } from "./tools/modifyHtmlHrefs";
8989
break early_exit;
9090
}
9191

92-
console.log(
93-
`DSFR distribution in ${pathRelative(process.cwd(), dsfrDirPath)} is up to date.`
94-
);
95-
9692
return;
9793
}
9894

@@ -170,10 +166,6 @@ import { modifyHtmlHrefs } from "./tools/modifyHtmlHrefs";
170166
return href;
171167
}
172168

173-
if (href.endsWith("icons.min.css")) {
174-
return href;
175-
}
176-
177169
const [urlWithoutQuery] = href.split("?");
178170

179171
return `${urlWithoutQuery}?v=${gouvFrDsfrVersion}`;
@@ -186,7 +178,7 @@ import { modifyHtmlHrefs } from "./tools/modifyHtmlHrefs";
186178

187179
fs.writeFileSync(htmlFilePath, Buffer.from(modifiedHtml, "utf8"));
188180
}
189-
})();
181+
}
190182

191183
function readAssetsImportFromDsfrCss(params: { dsfrSourceCode: string }): string[] {
192184
const { dsfrSourceCode } = params;
@@ -223,3 +215,7 @@ function getRepoIssueUrl() {
223215

224216
return `${reactDsfrRepoUrl}/issues`;
225217
}
218+
219+
if (require.main === module) {
220+
main(process.argv.slice(2));
221+
}

src/bin/only-include-used-icons.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env node
22

33
/**
4-
* This script is ran with `npx only-include-used-icons`
4+
* This script is ran with `npx react-dsfr include-used-icons`
55
* It scans your codebase to find which icons are used and only include those in the final build.
66
* Do do that it patches the node_modules/@codegouvfr/react-dsfr/dist/utility/icons/icons.css file
77
* and the public/dsfr/utility/icons/icons.css file (if applicable, not in Next.js for example).
@@ -232,6 +232,8 @@ async function getCommandContext(args: string[]): Promise<CommandContext> {
232232
if (!(await existsAsync(dsfrDirPath_static))) {
233233
return undefined;
234234
}
235+
236+
return dsfrDirPath_static;
235237
})();
236238

237239
const htmlFilePath = await (async () => {
@@ -275,6 +277,10 @@ async function getCommandContext(args: string[]): Promise<CommandContext> {
275277
"dirPath": projectDirPath,
276278
"returnedPathsType": "absolute",
277279
"getDoCrawlInDir": async ({ relativeDirPath }) => {
280+
if (relativeDirPath === "dist") {
281+
return false;
282+
}
283+
278284
if (pathBasename(relativeDirPath) === "node_modules") {
279285
return false;
280286
}
@@ -398,8 +404,8 @@ async function getCommandContext(args: string[]): Promise<CommandContext> {
398404
};
399405
}
400406

401-
async function main() {
402-
const commandContext = await getCommandContext(process.argv.slice(2));
407+
export async function main(args: string[]) {
408+
const commandContext = await getCommandContext(args);
403409

404410
const log = commandContext.isSilent ? undefined : console.log;
405411

@@ -470,7 +476,7 @@ async function main() {
470476
);
471477
}
472478

473-
log?.(`Found ${usedIconClassNames.length} used icons.`);
479+
log?.(`Found usage of ${usedIconClassNames.length} different icons.`);
474480

475481
const usedIcons = usedIconClassNames.map(className => {
476482
const icon = icons.find(({ prefix, iconId }) => `${prefix}${iconId}` === className);
@@ -501,7 +507,7 @@ async function main() {
501507

502508
await Promise.all(
503509
[commandContext.dsfrDirPath, commandContext.spaParams?.dsfrDirPath_static]
504-
.filter(dirPath => dirPath !== undefined)
510+
.filter(exclude(undefined))
505511
.map(async dsfrDirPath => {
506512
const cssFilePath = pathJoin(dsfrDirPath, iconsMinCssRelativePath);
507513

@@ -518,14 +524,15 @@ async function main() {
518524
);
519525

520526
if (!hasChanged) {
527+
log?.("No change since last run");
521528
return;
522529
}
523530

524531
await Promise.all([
525532
(async function generateUsedRemixiconFiles() {
526533
await Promise.all(
527534
[commandContext.dsfrDirPath, commandContext.spaParams?.dsfrDirPath_static]
528-
.filter(dirPath => dirPath !== undefined)
535+
.filter(exclude(undefined))
529536
.map(async dsfrDistDirPath => {
530537
const remixiconDirPath = pathJoin(dsfrDistDirPath, "icons", "remixicon");
531538

@@ -556,7 +563,8 @@ async function main() {
556563

557564
await Promise.all(
558565
usedIcons
559-
.filter(icon => icon.prefix === "fr-icon-")
566+
.map(icon => (icon.prefix !== "fr-icon-" ? undefined : icon))
567+
.filter(exclude(undefined))
560568
.map(({ svgRelativePath }) =>
561569
([commandContext.dsfrDirPath, dsfrDirPath_static] as const).map(
562570
baseDirPath =>
@@ -580,7 +588,7 @@ async function main() {
580588
const { modifiedHtml } = modifyHtmlHrefs({
581589
"html": html,
582590
"getModifiedHref": href => {
583-
if (!href.endsWith(iconsMinCssRelativePath.replace(/\\/g, "/"))) {
591+
if (!href.includes(iconsMinCssRelativePath.replace(/\\/g, "/"))) {
584592
return href;
585593
}
586594

@@ -622,5 +630,5 @@ async function main() {
622630
}
623631

624632
if (require.main === module) {
625-
main();
633+
main(process.argv.slice(2));
626634
}

src/bin/react-dsfr.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const [, , commandName, ...args] = process.argv;
2+
3+
(async () => {
4+
switch (commandName) {
5+
case "include-used-icons":
6+
{
7+
const { main } = await import("./only-include-used-icons");
8+
9+
await main(args);
10+
}
11+
break;
12+
case "copy-dsfr-to-public":
13+
{
14+
const { main } = await import("./copy-dsfr-to-public");
15+
16+
await main(args);
17+
}
18+
break;
19+
default:
20+
console.error(`Unknown command ${commandName}`);
21+
process.exit(-1);
22+
}
23+
})();

0 commit comments

Comments
 (0)