Skip to content

Instantly share code, notes, and snippets.

@Namaskar-1F64F
Last active October 4, 2023 08:47
Show Gist options
  • Save Namaskar-1F64F/45182ccb0075615d54b198da144e9ec3 to your computer and use it in GitHub Desktop.
Save Namaskar-1F64F/45182ccb0075615d54b198da144e9ec3 to your computer and use it in GitHub Desktop.
[pre-build] Optimizations for IPFS websites
const fs = require("fs");
const path = require("path");
const jscodeshift = require("jscodeshift");
const updatePackageJson = function (fileInfo) {
const json = JSON.parse(fileInfo.source);
// Check if the package is using react-scripts
const usesReactScripts =
(json.dependencies && json.dependencies["react-scripts"]) ||
(json.devDependencies && json.devDependencies["react-scripts"]);
if (usesReactScripts) {
json.homepage = ".";
}
return JSON.stringify(json, null, 2);
};
const updateNextConfig = function (fileInfo, api) {
const j = api.jscodeshift;
const root = j(fileInfo.source);
function addTrailingSlash(objExpression) {
let trailingSlashObjectFound = false;
objExpression.properties.forEach((property) => {
if (property.key.name === "trailingSlash") {
trailingSlashObjectFound = true;
}
});
if (!trailingSlashObjectFound) {
objExpression.properties.push(
j.property("init", j.identifier("trailingSlash"), j.literal(true))
);
}
}
function addOutputExport(objExpression) {
let outputObjectFound = false;
objExpression.properties.forEach((property) => {
if (property.key.name === "output") {
outputObjectFound = true;
property.value.value = 'export';
}
});
if (!outputObjectFound) {
objExpression.properties.push(
j.property("init", j.identifier("output"), j.literal("export"))
);
}
}
function updateImagesObject(objExpression) {
let imagesObjectFound = false;
objExpression.properties.forEach((property) => {
if (property.key.name === "images") {
imagesObjectFound = true;
let unoptimizedPropertyFound = false;
property.value.properties.forEach((prop) => {
if (prop.key.name === "unoptimized") {
unoptimizedPropertyFound = true;
prop.value.value = true;
}
});
if (!unoptimizedPropertyFound) {
property.value.properties.push(
j.property("init", j.identifier("unoptimized"), j.literal(true))
);
}
}
});
if (!imagesObjectFound) {
objExpression.properties.push(
j.property(
"init",
j.identifier("images"),
j.objectExpression([
j.property("init", j.identifier("unoptimized"), j.literal(true)),
])
)
);
}
}
// Case 1: Direct assignment to module.exports
root
.find(j.AssignmentExpression)
.filter(
(path) =>
path.value.left.type === "MemberExpression" &&
path.value.left.object.name === "module" &&
path.value.left.property.name === "exports" &&
(path.value.right.type === "ObjectExpression" ||
(path.value.right.type === "CallExpression" &&
path.value.right.arguments.length === 1 &&
path.value.right.arguments[0].type === "ObjectExpression"))
)
.forEach((path) => {
if (path.value.right.type === "ObjectExpression") {
updateImagesObject(path.value.right);
addTrailingSlash(path.value.right);
addOutputExport(path.value.right);
} else if (
path.value.right.type === "CallExpression" &&
path.value.right.arguments.length === 1 &&
path.value.right.arguments[0].type === "ObjectExpression"
) {
updateImagesObject(path.value.right.arguments[0]);
addTrailingSlash(path.value.right.arguments[0]);
addOutputExport(path.value.right.arguments[0]);
}
});
// Case 2: Separate nextConfig object
root
.find(j.VariableDeclarator)
.filter(
(path) =>
path.value.id.type === "Identifier" &&
path.value.id.name === "nextConfig" &&
path.value.init.type === "ObjectExpression"
)
.forEach((path) => {
updateImagesObject(path.value.init);
addTrailingSlash(path.value.init);
addOutputExport(path.value.init);
});
return root.toSource({ trailingComma: true });
};
const TRANSFORMATIONS = {
"../next.config.js": updateNextConfig,
"../next.config.mjs": updateNextConfig,
"../package.json": updatePackageJson,
};
const simpleDiff = (oldSource, newSource) => {
const oldLines = oldSource.split("\n");
const newLines = newSource.split("\n");
const diffLines = [];
for (let i = 0; i < Math.max(oldLines.length, newLines.length); i++) {
if (oldLines[i] !== newLines[i]) {
diffLines.push({
lineNumber: i + 1,
oldLine: oldLines[i] || "",
newLine: newLines[i] || "",
});
}
}
return diffLines;
};
const applyTransformations = () => {
for (const [fileName, transform] of Object.entries(TRANSFORMATIONS)) {
const fullPath = path.resolve(fileName);
if (fs.existsSync(fullPath)) {
console.log(`attempting transform on ${fullPath}`);
const fileInfo = {
path: fullPath,
source: fs.readFileSync(fullPath, "utf8"),
};
let newSource;
if (fullPath.endsWith("package.json")) {
newSource = updatePackageJson(fileInfo);
} else {
const api = { jscodeshift };
newSource = transform(fileInfo, api);
}
const changes = simpleDiff(fileInfo.source, newSource);
if (changes.length > 0) {
console.log(`Changes in ${fullPath}:`);
changes.forEach((change) => {
console.log("- " + change.oldLine);
console.log("+ " + change.newLine);
});
}
fs.writeFileSync(fullPath, newSource, "utf8");
} else {
console.log(`skipping transform on ${fullPath} as it was not found`);
}
}
};
applyTransformations();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment