First draft
This commit is contained in:
parent
3af3178d42
commit
83755d1f5f
2011
package-lock.json
generated
2011
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
22
package.json
22
package.json
@ -1,16 +1,21 @@
|
|||||||
{
|
{
|
||||||
"name": "keycloak-react-theming",
|
"name": "keycloak-react-theming",
|
||||||
"version": "0.0.2",
|
"version": "0.0.2",
|
||||||
"description": "Provides a way to customise Keycloak login and register pages with React",
|
"description": "Provides a way to customize Keycloak login and register pages with React",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/garronej/keycloak-react-theming.git"
|
"url": "git://github.com/garronej/keycloak-react-theming.git"
|
||||||
},
|
},
|
||||||
"main": "dist/index.js",
|
"main": "src/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"babel": {
|
||||||
|
"presets": [
|
||||||
|
"@babel/preset-env",
|
||||||
|
"@babel/preset-react"
|
||||||
|
]
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node dist/test/",
|
"start": "webpack-dev-server --open",
|
||||||
"build": "tsc",
|
"create": "webpack",
|
||||||
"enable_short_import_path": "npm run build && denoify_enable_short_npm_import_path"
|
"enable_short_import_path": "npm run build && denoify_enable_short_npm_import_path"
|
||||||
},
|
},
|
||||||
"author": "u/garronej",
|
"author": "u/garronej",
|
||||||
@ -27,8 +32,11 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^10.0.0",
|
"@types/node": "^10.0.0",
|
||||||
"denoify": "^0.6.4",
|
"denoify": "^0.6.4",
|
||||||
"evt": "^1.8.11",
|
"evt": "beta",
|
||||||
"lint-staged": "^10.5.4",
|
|
||||||
"typescript": "^4.1.5"
|
"typescript": "^4.1.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cheerio": "^1.0.0-rc.5",
|
||||||
|
"node-html-parser": "^2.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
30
res/index.html
Normal file
30
res/index.html
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<meta name="description" content="Web site created using create-react-app" />
|
||||||
|
<link rel="apple-touch-icon" href="/logo192.png" />
|
||||||
|
<link rel="manifest" href="/manifest.json" />
|
||||||
|
<title>React App</title>
|
||||||
|
<link href="/static/css/main.8c8b27cf.chunk.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script>
|
||||||
|
function f() {
|
||||||
|
return a.p + "static/js/" + ({}[e] || e) + "." + {
|
||||||
|
3: "0664cdc0"
|
||||||
|
}[e] + ".chunk.js"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script src="/static/js/2.0f3a6c43.chunk.js"></script>
|
||||||
|
<script src="/static/js/main.94e9b83c.chunk.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
0
res/static/css/main.8c8b27cf.chunk.css
Normal file
0
res/static/css/main.8c8b27cf.chunk.css
Normal file
13
res/static/js/2.0f3a6c43.chunk.js
Normal file
13
res/static/js/2.0f3a6c43.chunk.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
return a.p + "static/js/" + ({}[e] || e) + "." + {
|
||||||
|
3: "0664cdc0"
|
||||||
|
}[e] + ".chunk.js"
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
return a.p + "static/js/" + ({}[e] || e) + "." + {
|
||||||
|
3: "0664cdc0"
|
||||||
|
}[e] + ".chunk.js"
|
||||||
|
}
|
13
res/static/js/2.0f3a6c43.chunk_another.js
Normal file
13
res/static/js/2.0f3a6c43.chunk_another.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
return a.p + "static/js/" + ({}[e] || e) + "." + {
|
||||||
|
3: "0664cdc0"
|
||||||
|
}[e] + ".chunk.js"
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
return a.p + "static/js/" + ({}[e] || e) + "." + {
|
||||||
|
3: "0664cdc0"
|
||||||
|
}[e] + ".chunk.js"
|
||||||
|
}
|
105
src/bin/generateFtl.ts
Normal file
105
src/bin/generateFtl.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import cheerio from "cheerio";
|
||||||
|
import {
|
||||||
|
replaceImportFromStaticInJsCode,
|
||||||
|
generateCssCodeToDefineGlobals
|
||||||
|
} from "./replaceImportFromStatic";
|
||||||
|
|
||||||
|
export function generateFtlFilesCodeFactory(
|
||||||
|
params: {
|
||||||
|
ftlValuesGlobalName: string;
|
||||||
|
cssGlobalsToDefine: Record<string, string>;
|
||||||
|
indexHtmlCode: string;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
|
const { ftlValuesGlobalName, cssGlobalsToDefine, indexHtmlCode } = params;
|
||||||
|
|
||||||
|
const $ = cheerio.load(indexHtmlCode);
|
||||||
|
|
||||||
|
$("script:not([src])").each((...[, element]) => {
|
||||||
|
|
||||||
|
const { fixedJsCode } = replaceImportFromStaticInJsCode({
|
||||||
|
ftlValuesGlobalName,
|
||||||
|
"jsCode": $(element).html()!
|
||||||
|
});
|
||||||
|
|
||||||
|
$(element).html(fixedJsCode);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
([
|
||||||
|
["link", "href"],
|
||||||
|
["script", "src"],
|
||||||
|
] as const).forEach(([selector, attrName]) =>
|
||||||
|
$(selector).each((...[, element]) => {
|
||||||
|
|
||||||
|
const href = $(element).attr(attrName);
|
||||||
|
|
||||||
|
if (!href?.startsWith("/")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(element).attr(attrName, "${url.resourcesPath}" + href);
|
||||||
|
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$("head").prepend(
|
||||||
|
[
|
||||||
|
'',
|
||||||
|
'<style>',
|
||||||
|
generateCssCodeToDefineGlobals(
|
||||||
|
{ cssGlobalsToDefine }
|
||||||
|
).cssCodeToPrependInHead,
|
||||||
|
'</style>',
|
||||||
|
'',
|
||||||
|
'<script>',
|
||||||
|
' Object.assign(',
|
||||||
|
` window.${ftlValuesGlobalName},`,
|
||||||
|
' {',
|
||||||
|
' "url": {',
|
||||||
|
' "loginAction": "${url.loginAction}",',
|
||||||
|
' "resourcesPath": "${url.resourcesPath}"',
|
||||||
|
' }',
|
||||||
|
' }',
|
||||||
|
' });',
|
||||||
|
'</script>',
|
||||||
|
''
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const partiallyFixedIndexHtmlCode = $.html();
|
||||||
|
|
||||||
|
function generateFtlFilesCode(
|
||||||
|
params: {
|
||||||
|
pageBasename: "login.ftl" | "register.ftl"
|
||||||
|
}
|
||||||
|
): { ftlCode: string; } {
|
||||||
|
|
||||||
|
const { pageBasename } = params;
|
||||||
|
|
||||||
|
const $ = cheerio.load(partiallyFixedIndexHtmlCode);
|
||||||
|
|
||||||
|
$("head").prepend(
|
||||||
|
[
|
||||||
|
'',
|
||||||
|
'<script>',
|
||||||
|
` window.${ftlValuesGlobalName} = { "pageBasename": "${pageBasename}" };'`,
|
||||||
|
'</script>',
|
||||||
|
''
|
||||||
|
].join("\n"),
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
return { "ftlCode": $.html() };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return { generateFtlFilesCode };
|
||||||
|
|
||||||
|
|
||||||
|
}
|
80
src/bin/main.ts
Normal file
80
src/bin/main.ts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
|
||||||
|
import { transformCodebase } from "../tools/transformCodebase";
|
||||||
|
import * as fs from "fs";
|
||||||
|
import { join as pathJoin } from "path";
|
||||||
|
import { assert } from "evt/tools/typeSafety/assert";
|
||||||
|
import {
|
||||||
|
replaceImportFromStaticInCssCode,
|
||||||
|
replaceImportFromStaticInJsCode
|
||||||
|
} from "./replaceImportFromStatic";
|
||||||
|
import { generateFtlFilesCodeFactory } from "./generateFtl";
|
||||||
|
|
||||||
|
|
||||||
|
const reactAppBuildDirPath = pathJoin(__dirname, "build");
|
||||||
|
|
||||||
|
assert(
|
||||||
|
fs.existsSync(reactAppBuildDirPath),
|
||||||
|
"Run 'react-script build' first (the build dir should be present)"
|
||||||
|
);
|
||||||
|
|
||||||
|
const keycloakDir = pathJoin(reactAppBuildDirPath, "..", "keycloak_build");
|
||||||
|
|
||||||
|
let allCssGlobalsToDefine: Record<string, string> = {};
|
||||||
|
|
||||||
|
const ftlValuesGlobalName = "keycloakFtlValues";
|
||||||
|
|
||||||
|
|
||||||
|
transformCodebase({
|
||||||
|
"destDirPath": pathJoin(keycloakDir, "login", "resources"),
|
||||||
|
"srcDirPath": reactAppBuildDirPath,
|
||||||
|
"transformSourceCodeString": ({ filePath, sourceCode }) => {
|
||||||
|
|
||||||
|
if (/\.css?$/i.test(filePath)) {
|
||||||
|
|
||||||
|
const { cssGlobalsToDefine, fixedCssCode } = replaceImportFromStaticInCssCode(
|
||||||
|
{ "cssCode": sourceCode.toString("utf8") }
|
||||||
|
);
|
||||||
|
|
||||||
|
allCssGlobalsToDefine = {
|
||||||
|
...allCssGlobalsToDefine,
|
||||||
|
...cssGlobalsToDefine
|
||||||
|
};
|
||||||
|
|
||||||
|
return { "modifiedSourceCode": Buffer.from(fixedCssCode, "utf8") };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/\.js?$/i.test(filePath)) {
|
||||||
|
|
||||||
|
const { fixedJsCode } = replaceImportFromStaticInJsCode({
|
||||||
|
"jsCode": sourceCode.toString("utf8"),
|
||||||
|
ftlValuesGlobalName
|
||||||
|
});
|
||||||
|
|
||||||
|
return { "modifiedSourceCode": Buffer.from(fixedJsCode, "utf8") };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return { "modifiedSourceCode": sourceCode };
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const { generateFtlFilesCode } = generateFtlFilesCodeFactory({
|
||||||
|
"cssGlobalsToDefine": allCssGlobalsToDefine,
|
||||||
|
ftlValuesGlobalName,
|
||||||
|
"indexHtmlCode": fs.readFileSync(
|
||||||
|
pathJoin(reactAppBuildDirPath, "index.html")
|
||||||
|
).toString("utf8")
|
||||||
|
});
|
||||||
|
|
||||||
|
(["login.ftl", "register.ftl"] as const).forEach(pageBasename => {
|
||||||
|
|
||||||
|
const { ftlCode } = generateFtlFilesCode({ pageBasename });
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
pathJoin(keycloakDir, "login", pageBasename),
|
||||||
|
Buffer.from(ftlCode, "utf8")
|
||||||
|
)
|
||||||
|
|
||||||
|
});
|
91
src/bin/replaceImportFromStatic.ts
Normal file
91
src/bin/replaceImportFromStatic.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
import * as crypto from "crypto";
|
||||||
|
|
||||||
|
export function replaceImportFromStaticInJsCode(
|
||||||
|
params: {
|
||||||
|
ftlValuesGlobalName: string;
|
||||||
|
jsCode: string;
|
||||||
|
}
|
||||||
|
): { fixedJsCode: string; } {
|
||||||
|
|
||||||
|
const { jsCode, ftlValuesGlobalName } = params;
|
||||||
|
|
||||||
|
const fixedJsCode = jsCode!.replace(
|
||||||
|
/"static\//g,
|
||||||
|
`window.${ftlValuesGlobalName}.url.resourcesPath.replace(/^\//,"") + "/" + "static/`
|
||||||
|
);
|
||||||
|
|
||||||
|
return { fixedJsCode };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function replaceImportFromStaticInCssCode(
|
||||||
|
params: {
|
||||||
|
cssCode: string;
|
||||||
|
}
|
||||||
|
): {
|
||||||
|
fixedCssCode: string;
|
||||||
|
cssGlobalsToDefine: Record<string, string>;
|
||||||
|
} {
|
||||||
|
|
||||||
|
const { cssCode } = params;
|
||||||
|
|
||||||
|
const cssGlobalsToDefine: Record<string, string> = {};
|
||||||
|
|
||||||
|
new Set(cssCode.match(/(url\(\/[^)]+\))/g) ?? [])
|
||||||
|
.forEach(match =>
|
||||||
|
cssGlobalsToDefine[
|
||||||
|
"url" + crypto
|
||||||
|
.createHash("sha256")
|
||||||
|
.update(match)
|
||||||
|
.digest("hex")
|
||||||
|
.substring(0, 15)
|
||||||
|
] = match
|
||||||
|
);
|
||||||
|
|
||||||
|
let fixedCssCode = cssCode;
|
||||||
|
|
||||||
|
Object.keys(cssGlobalsToDefine).forEach(
|
||||||
|
cssVariableName =>
|
||||||
|
//NOTE: split/join pattern ~ replace all
|
||||||
|
fixedCssCode =
|
||||||
|
fixedCssCode.split(cssGlobalsToDefine[cssVariableName])
|
||||||
|
.join(`var(--${cssVariableName})`)
|
||||||
|
);
|
||||||
|
|
||||||
|
return { fixedCssCode, cssGlobalsToDefine };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateCssCodeToDefineGlobals(
|
||||||
|
params: {
|
||||||
|
cssGlobalsToDefine: Record<string, string>;
|
||||||
|
}
|
||||||
|
): {
|
||||||
|
cssCodeToPrependInHead: string;
|
||||||
|
} {
|
||||||
|
|
||||||
|
const { cssGlobalsToDefine } = params;
|
||||||
|
|
||||||
|
return {
|
||||||
|
"cssCodeToPrependInHead": [
|
||||||
|
":root {",
|
||||||
|
...Object.keys(cssGlobalsToDefine)
|
||||||
|
.map(cssVariableName => [
|
||||||
|
`--${cssVariableName}:`,
|
||||||
|
[
|
||||||
|
"url(",
|
||||||
|
"${url.resourcesPath}" +
|
||||||
|
cssGlobalsToDefine[cssVariableName].match(/^url\(([^)]+)\)$/)![1],
|
||||||
|
")"
|
||||||
|
].join("")
|
||||||
|
].join(" "))
|
||||||
|
.map(line => " " + line),
|
||||||
|
"}"
|
||||||
|
].join("\n")
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
|||||||
export { myFunction } from "./myFunction";
|
|
||||||
export { myObject } from "./myObject";
|
|
@ -1,3 +0,0 @@
|
|||||||
export function myFunction() {
|
|
||||||
return Promise.resolve(["a", "b", "c"]);
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
import { toUpperCase } from "./tools/toUpperCase";
|
|
||||||
|
|
||||||
export const myObject = { "p": toUpperCase("foo") };
|
|
@ -1,5 +0,0 @@
|
|||||||
import { getProjectRoot } from "../tools/getProjectRoot";
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`Project root path: ${getProjectRoot()} does it seems right ? If yes then PASS`,
|
|
||||||
);
|
|
@ -1,41 +0,0 @@
|
|||||||
//This will not run on deno, we need a separate test runner for Deno (./mod.ts).
|
|
||||||
|
|
||||||
import * as child_process from "child_process";
|
|
||||||
import * as path from "path";
|
|
||||||
import { Deferred } from "evt/tools/Deferred";
|
|
||||||
|
|
||||||
const names = ["myFunction", "myObject", "getProjectRoot"];
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
if (!!process.env.FORK) {
|
|
||||||
process.once("unhandledRejection", error => {
|
|
||||||
throw error;
|
|
||||||
});
|
|
||||||
|
|
||||||
require(process.env.FORK);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const name of names) {
|
|
||||||
console.log(`Running: ${name}`);
|
|
||||||
|
|
||||||
const dExitCode = new Deferred<number>();
|
|
||||||
|
|
||||||
child_process
|
|
||||||
.fork(__filename, undefined, {
|
|
||||||
"env": { "FORK": path.join(__dirname, name) },
|
|
||||||
})
|
|
||||||
.on("message", console.log)
|
|
||||||
.once("exit", code => dExitCode.resolve(code ?? 1));
|
|
||||||
|
|
||||||
const exitCode = await dExitCode.pr;
|
|
||||||
|
|
||||||
if (exitCode !== 0) {
|
|
||||||
console.log(`${name} exited with error code: ${exitCode}`);
|
|
||||||
process.exit(exitCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("\n");
|
|
||||||
}
|
|
||||||
})();
|
|
@ -1,16 +0,0 @@
|
|||||||
import { myFunction } from "..";
|
|
||||||
import { getPromiseAssertionApi } from "evt/tools/testing";
|
|
||||||
|
|
||||||
const { mustResolve } = getPromiseAssertionApi({
|
|
||||||
"takeIntoAccountArraysOrdering": true,
|
|
||||||
});
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
await mustResolve({
|
|
||||||
"promise": myFunction(),
|
|
||||||
"expectedData": ["a", "b", "c"],
|
|
||||||
"delay": 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("PASS");
|
|
||||||
})();
|
|
@ -1,7 +0,0 @@
|
|||||||
import { assert } from "evt/tools/typeSafety";
|
|
||||||
import * as inDepth from "evt/tools/inDepth";
|
|
||||||
import { myObject } from "..";
|
|
||||||
|
|
||||||
assert(inDepth.same(myObject, { "p": "FOO" }));
|
|
||||||
|
|
||||||
console.log("PASS");
|
|
50
src/test/replaceImportFromStatic.ts
Normal file
50
src/test/replaceImportFromStatic.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
|
||||||
|
import {
|
||||||
|
replaceImportFromStaticInJsCode,
|
||||||
|
replaceImportFromStaticInCssCode,
|
||||||
|
generateCssCodeToDefineGlobals
|
||||||
|
} from "../bin/replaceImportFromStatic";
|
||||||
|
|
||||||
|
const { fixedJsCode } = replaceImportFromStaticInJsCode({
|
||||||
|
"ftlValuesGlobalName": "keycloakFtlValues",
|
||||||
|
"jsCode": `
|
||||||
|
function f() {
|
||||||
|
return a.p + "static/js/" + ({}[e] || e) + "." + {
|
||||||
|
3: "0664cdc0"
|
||||||
|
}[e] + ".chunk.js"
|
||||||
|
}
|
||||||
|
|
||||||
|
function f2() {
|
||||||
|
return a.p +"static/js/" + ({}[e] || e) + "." + {
|
||||||
|
3: "0664cdc0"
|
||||||
|
}[e] + ".chunk.js"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log({ fixedJsCode });
|
||||||
|
|
||||||
|
const { fixedCssCode, cssGlobalsToDefine } = replaceImportFromStaticInCssCode({
|
||||||
|
"cssCode": `
|
||||||
|
|
||||||
|
.my-div {
|
||||||
|
background: url(/logo192.png) no-repeat center center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-div2 {
|
||||||
|
background: url(/logo192.png) no-repeat center center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-div {
|
||||||
|
background-image: url(/static/media/something.svg);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
console.log({ fixedCssCode, cssGlobalsToDefine });
|
||||||
|
|
||||||
|
|
||||||
|
const { cssCodeToPrependInHead } = generateCssCodeToDefineGlobals({ cssGlobalsToDefine });
|
||||||
|
|
||||||
|
console.log({ cssCodeToPrependInHead });
|
@ -1,19 +0,0 @@
|
|||||||
import * as fs from "fs";
|
|
||||||
import * as path from "path";
|
|
||||||
|
|
||||||
function getProjectRootRec(dirPath: string): string {
|
|
||||||
if (fs.existsSync(path.join(dirPath, "package.json"))) {
|
|
||||||
return dirPath;
|
|
||||||
}
|
|
||||||
return getProjectRootRec(path.join(dirPath, ".."));
|
|
||||||
}
|
|
||||||
|
|
||||||
let result: string | undefined = undefined;
|
|
||||||
|
|
||||||
export function getProjectRoot(): string {
|
|
||||||
if (result !== undefined) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (result = getProjectRootRec(__dirname));
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
export function toUpperCase(str: string): string {
|
|
||||||
return str.toUpperCase();
|
|
||||||
}
|
|
62
src/tools/transformCodebase.ts
Normal file
62
src/tools/transformCodebase.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
import { crawl } from "denoify/tools/crawl";
|
||||||
|
import { createDirectoryIfNotExistsRecursive } from "denoify/tools/createDirectoryIfNotExistsRecursive";
|
||||||
|
|
||||||
|
/** Apply a transformation function to every file of directory */
|
||||||
|
export async function transformCodebase(
|
||||||
|
params: {
|
||||||
|
srcDirPath: string;
|
||||||
|
destDirPath: string;
|
||||||
|
transformSourceCodeString: (params: {
|
||||||
|
sourceCode: Buffer;
|
||||||
|
filePath: string;
|
||||||
|
}) => {
|
||||||
|
modifiedSourceCode: Buffer;
|
||||||
|
newFileName?: string;
|
||||||
|
} | undefined;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
|
||||||
|
const { srcDirPath, destDirPath, transformSourceCodeString } = params;
|
||||||
|
|
||||||
|
for (const file_relative_path of crawl(srcDirPath)) {
|
||||||
|
|
||||||
|
const filePath = path.join(srcDirPath, file_relative_path);
|
||||||
|
|
||||||
|
const transformSourceCodeStringResult = transformSourceCodeString({
|
||||||
|
"sourceCode": fs.readFileSync(filePath),
|
||||||
|
"filePath": path.join(srcDirPath, file_relative_path)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (transformSourceCodeStringResult === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
await createDirectoryIfNotExistsRecursive(
|
||||||
|
path.dirname(
|
||||||
|
path.join(
|
||||||
|
destDirPath,
|
||||||
|
file_relative_path
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const { newFileName, modifiedSourceCode } = transformSourceCodeStringResult;
|
||||||
|
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(
|
||||||
|
path.dirname(path.join(destDirPath, file_relative_path)),
|
||||||
|
newFileName ?? path.basename(file_relative_path)
|
||||||
|
),
|
||||||
|
modifiedSourceCode
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user