UNPKG

@codegouvfr/react-dsfr

Version:

French State Design System React integration library

222 lines (169 loc) 6.87 kB
#!/usr/bin/env node /** * This script is ran with `npx react-dsfr copy-dsfr-to-public` * It takes one optional arguments (for NX monorepos): * - `--projectDir <path>` to specify the project directory. Default to the current working directory. * This can be used in monorepos to specify the react project directory. */ import { join as pathJoin, resolve as pathResolve } from "path"; import * as fs from "fs"; import { getProjectRoot } from "./tools/getProjectRoot"; import yargsParser from "yargs-parser"; import { getAbsoluteAndInOsFormatPath } from "./tools/getAbsoluteAndInOsFormatPath"; import { readPublicDirPath } from "./readPublicDirPath"; import { transformCodebase } from "./tools/transformCodebase"; import { assert } from "tsafe/assert"; import { modifyHtmlHrefs } from "./tools/modifyHtmlHrefs"; export async function main(args: string[]) { const argv = yargsParser(args); const projectDirPath: string = (() => { read_from_argv: { const arg = argv["projectDir"]; if (arg === undefined) { break read_from_argv; } return getAbsoluteAndInOsFormatPath({ "pathIsh": arg, "cwd": process.cwd() }); } return process.cwd(); })(); const publicDirPath = await readPublicDirPath({ projectDirPath }); const htmlFilePath = await (async () => { vite: { const filePath = pathJoin(projectDirPath, "index.html"); if (!fs.existsSync(filePath)) { break vite; } return filePath; } cra: { const filePath = pathJoin(publicDirPath, "index.html"); if (!fs.existsSync(filePath)) { break cra; } return filePath; } assert(false, "Can't locate your index.html file."); })(); if (!fs.existsSync(publicDirPath)) { console.error(`Can't locate your public directory.`); process.exit(-1); } const dsfrDirPath = pathJoin(publicDirPath, "dsfr"); const gouvFrDsfrVersion: string = JSON.parse( fs.readFileSync(pathJoin(getProjectRoot(), "package.json")).toString("utf8") )["devDependencies"]["@gouvfr/dsfr"]; const versionFilePath = pathJoin(dsfrDirPath, "version.txt"); early_exit: { if (!fs.existsSync(dsfrDirPath)) { break early_exit; } if (!fs.existsSync(versionFilePath)) { break early_exit; } const currentVersion = fs.readFileSync(versionFilePath).toString("utf8"); if (currentVersion !== gouvFrDsfrVersion) { fs.rmSync(dsfrDirPath, { "recursive": true, "force": true }); break early_exit; } return; } fs.mkdirSync(dsfrDirPath, { "recursive": true }); fs.writeFileSync(pathJoin(dsfrDirPath, ".gitignore"), Buffer.from("*", "utf8")); const dsfrDistNodeModulesDirPath = (function dsfrDistNodeModulesDirPath(depth: number): string { const parentProjectDirPath = pathResolve( pathJoin(...[projectDirPath, ...new Array(depth).fill("..")]) ); const dsfrDirPathInNodeModules = pathJoin( ...[parentProjectDirPath, "node_modules", "@codegouvfr", "react-dsfr", "dsfr"] ); if (!fs.existsSync(dsfrDirPathInNodeModules)) { if (parentProjectDirPath === "/") { console.error( [ "Can't find dsfr directory", `please submit an issue about it here ${getRepoIssueUrl()}` ].join(" ") ); process.exit(-1); } return dsfrDistNodeModulesDirPath(depth + 1); } return dsfrDirPathInNodeModules; })(0); { const dsfrMinCssFileRelativePath = "dsfr.min.css"; const usedAssetsRelativeFilePaths = new Set( readAssetsImportFromDsfrCss({ "dsfrSourceCode": fs .readFileSync(pathJoin(dsfrDistNodeModulesDirPath, dsfrMinCssFileRelativePath)) .toString("utf8") }) ); const fileToKeepRelativePaths = new Set([ pathJoin("favicon", "apple-touch-icon.png"), pathJoin("favicon", "favicon.svg"), pathJoin("favicon", "favicon.ico"), pathJoin("favicon", "manifest.webmanifest"), pathJoin("utility", "icons", "icons.min.css"), dsfrMinCssFileRelativePath ]); transformCodebase({ "srcDirPath": dsfrDistNodeModulesDirPath, "destDirPath": dsfrDirPath, "transformSourceCode": ({ fileRelativePath, sourceCode }) => { if ( fileToKeepRelativePaths.has(fileRelativePath) || usedAssetsRelativeFilePaths.has(fileRelativePath) ) { return { "modifiedSourceCode": sourceCode }; } } }); } fs.writeFileSync(versionFilePath, Buffer.from(gouvFrDsfrVersion, "utf8")); add_version_query_params_in_html_imports: { const { modifiedHtml } = modifyHtmlHrefs({ "html": fs.readFileSync(htmlFilePath).toString("utf8"), "getModifiedHref": href => { if (!href.includes("/dsfr/")) { return href; } const [urlWithoutQuery] = href.split("?"); return `${urlWithoutQuery}?v=${gouvFrDsfrVersion}`; } }); if (htmlFilePath === modifiedHtml) { break add_version_query_params_in_html_imports; } fs.writeFileSync(htmlFilePath, Buffer.from(modifiedHtml, "utf8")); } } function readAssetsImportFromDsfrCss(params: { dsfrSourceCode: string }): string[] { const { dsfrSourceCode } = params; const fileRelativePaths = [/url\("([^"]+)"\)/g, /url\('([^']+)'\)/g, /url\(([^)]+)\)/g] .map(regex => { const fileRelativePaths: string[] = []; dsfrSourceCode.replace(regex, (...[, relativeFilePath]) => { if (relativeFilePath.startsWith("data:")) { return ""; } fileRelativePaths.push(relativeFilePath); return ""; }); return fileRelativePaths; }) .flat(); assert(fileRelativePaths.length !== 0); return fileRelativePaths; } function getRepoIssueUrl() { const reactDsfrRepoUrl = JSON.parse( fs.readFileSync(pathJoin(getProjectRoot(), "package.json")).toString("utf8") ) ["repository"]["url"].replace(/^git/, "https:") .replace(/\.git$/, ""); return `${reactDsfrRepoUrl}/issues`; } if (require.main === module) { main(process.argv.slice(2)); }