Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "nextjs",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "next dev",
"build": "next build",
Expand Down
11 changes: 5 additions & 6 deletions next/tailwind.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import typography from '@tailwindcss/typography';
import svgToDataUri from 'mini-svg-data-uri';
import type { Config } from 'tailwindcss';

const {
default: flattenColorPalette,
} = require('tailwindcss/lib/util/flattenColorPalette');
import tailwindcssAnimate from 'tailwindcss-animate';
import flattenColorPalette from 'tailwindcss/lib/util/flattenColorPalette';

const config: Config = {
content: [
Expand Down Expand Up @@ -50,8 +49,8 @@ const config: Config = {
},
},
plugins: [
require('tailwindcss-animate'),
require('@tailwindcss/typography'),
tailwindcssAnimate,
typography,
addVariablesForColors,
function ({ matchUtilities, theme }: any) {
matchUtilities(
Expand Down
7 changes: 7 additions & 0 deletions next/types/tailwind-internal.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
declare module 'tailwindcss/lib/util/flattenColorPalette' {
type FlattenedColors = Record<string, string>;

export default function flattenColorPalette(
colors: Record<string, unknown>
): FlattenedColors;
}
3 changes: 3 additions & 0 deletions strapi/config/database.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import path from 'path';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

export default ({ env }) => {
const client = env('DATABASE_CLIENT', 'sqlite');
Expand Down
5 changes: 3 additions & 2 deletions strapi/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"name": "strapi",
"private": true,
"version": "0.1.0",
"version": "1.0.0",
"type": "module",
"description": "A Strapi application",
"scripts": {
"develop": "strapi develop",
Expand All @@ -11,7 +12,7 @@
"strapi": "strapi",
"deploy": "strapi deploy",
"seed": "strapi import -f ./data/export_20250116105447.tar.gz",
"postinstall": "node ./scripts/updateUuid.js"
"postinstall": "node ./scripts/updateUuid.js && node ./scripts/patch-cjs-deps-esm.js && node ./scripts/patch-strapi-esm.js"
},
"devDependencies": {
"@types/node": "^20",
Expand Down
122 changes: 122 additions & 0 deletions strapi/scripts/patch-cjs-deps-esm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import fs from 'node:fs';
import { createRequire } from 'node:module';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

/*
* Why this patch exists
* ---------------------
*
* This Strapi app runs as ESM (`"type": "module"` in `strapi/package.json`)
* and compiles its server TypeScript with `module: "NodeNext"`. That makes
* Strapi load its own `.mjs` server build paths at runtime.
*
* Strapi 5.47.1 publishes ESM files, but some of those files still import
* CommonJS-only dependencies as if Node could provide native ESM named exports
* from them. Two examples seen during the migration:
*
* - `@strapi/core/dist/Strapi.mjs` imports named utilities from `lodash/fp`.
* - `@strapi/core/dist/loaders/apis.mjs` imports `existsSync` from `fs-extra`.
*
* Both packages are fundamentally CommonJS in the paths Strapi uses:
*
* - `lodash/fp` is a directory/subpath backed by `fp.js`, and Node's ESM
* resolver does not support extensionless directory imports unless the
* package provides a matching `exports` map.
* - `lodash` and `fs-extra` expose their runtime API as a CommonJS object.
* Node can provide that object as a default import, but it does not synthesize
* reliable named exports for these packages in the way Strapi's `.mjs` files
* currently expect.
*
* Without this patch, Strapi starts failing with errors such as:
*
* - `ERR_UNSUPPORTED_DIR_IMPORT: Directory import '.../lodash/fp' is not supported`
* - `SyntaxError: The requested module 'lodash/fp' does not provide an export named 'get'`
* - `SyntaxError: The requested module 'fs-extra' does not provide an export named 'existsSync'`
*
* What this script does
* ---------------------
*
* During `postinstall`, we generate tiny ESM wrapper files inside the installed
* dependency folders. Each wrapper default-imports the original CommonJS file,
* re-exports that default, and then re-exports every valid property name as a
* named ESM export. We also patch the dependency `package.json` `exports` map so
* Node resolves Strapi's imports to those wrappers.
*
* This is intentionally a local install-time compatibility shim, not a source
* code abstraction. It should be removed once Strapi publishes ESM-compatible
* imports for these dependencies or once the project upgrades to a Strapi
* version where `yarn start` works with this app's `"type": "module"` and
* `module: "NodeNext"` settings without patching `node_modules`.
*/

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const strapiRoot = path.join(__dirname, '..');
const require = createRequire(import.meta.url);

const isValidIdentifier = (key) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key);

const patchPackage = (packageName, entries) => {
const packageDir = path.join(strapiRoot, 'node_modules', packageName);
const packageJsonPath = path.join(packageDir, 'package.json');

if (fs.existsSync(packageJsonPath) === false) {
return;
}

const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
const exportsMap = {};

for (const entry of entries) {
const cjsPath = path.join(packageDir, entry.cjsFile);
const mjsPath = path.join(packageDir, entry.mjsFile);

if (fs.existsSync(cjsPath) === false) {
continue;
}

const cjsModule = require(cjsPath);
const keys = Object.keys(cjsModule);
const namedExports = keys
.filter(isValidIdentifier)
.map((key) => `export const ${key} = cjsModule.${key};`)
.join('\n');

const importPath = `./${path
.relative(path.dirname(mjsPath), cjsPath)
.replace(/\\/g, '/')}`;

const wrapper = `import cjsModule from '${importPath}';

export default cjsModule;

${namedExports}
`;

fs.writeFileSync(mjsPath, wrapper);
exportsMap[entry.exportSubpath] = `./${entry.mjsFile.replace(/\\/g, '/')}`;
}

if (packageName === 'lodash') {
exportsMap['./*'] = './*.js';
}

if (packageName === 'fs-extra') {
exportsMap['./esm'] = './lib/esm.mjs';
}

packageJson.exports = exportsMap;
fs.writeFileSync(
packageJsonPath,
`${JSON.stringify(packageJson, null, 2)}\n`
);
};

patchPackage('lodash', [
{ cjsFile: 'lodash.js', mjsFile: 'lodash.mjs', exportSubpath: '.' },
{ cjsFile: 'fp.js', mjsFile: 'fp.mjs', exportSubpath: './fp' },
]);

patchPackage('fs-extra', [
{ cjsFile: 'lib/index.js', mjsFile: 'lib/index.mjs', exportSubpath: '.' },
]);
46 changes: 46 additions & 0 deletions strapi/scripts/patch-strapi-esm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const strapiRoot = path.join(__dirname, '..');

const dirnamePolyfill = `import { fileURLToPath as __strapiFileURLToPath } from 'node:url';
import { dirname as __strapiDirname } from 'node:path';
const __dirname = __strapiDirname(__strapiFileURLToPath(import.meta.url));
`;

const filesToPatch = [
'node_modules/@strapi/core/dist/ee/license.mjs',
'node_modules/@strapi/generators/dist/index.mjs',
'node_modules/@strapi/admin/dist/server/server/src/routes/serve-admin-panel.mjs',
'node_modules/@strapi/plugin-users-permissions/dist/server/register.mjs',
];

for (const relativePath of filesToPatch) {
const filePath = path.join(strapiRoot, relativePath);

if (fs.existsSync(filePath) === false) {
continue;
}

const source = fs.readFileSync(filePath, 'utf-8');

if (source.includes('__strapiFileURLToPath') === true) {
continue;
}

if (source.includes('__dirname') === false) {
continue;
}

const lastImportIndex = source.lastIndexOf('import ');
const nextLineIndex = source.indexOf('\n', lastImportIndex);

if (lastImportIndex === -1 || nextLineIndex === -1) {
continue;
}

const patched = `${source.slice(0, nextLineIndex + 1)}${dirnamePolyfill}${source.slice(nextLineIndex + 1)}`;
fs.writeFileSync(filePath, patched);
}
16 changes: 11 additions & 5 deletions strapi/scripts/prefillLoginFields.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const fs = require('fs');
const path = require('path');
import fs from 'node:fs';
import path from 'node:path';

// Base directory and target files
const directoryPath =
Expand All @@ -24,7 +24,7 @@ let found = 0;
targetFiles.forEach((file) => {
const filePath = path.join(directoryPath, file);

if (!fs.existsSync(filePath)) {
if (fs.existsSync(filePath) === false) {
console.log(`⚠️ No matching file found for: ${file}`);
return;
}
Expand All @@ -35,7 +35,11 @@ targetFiles.forEach((file) => {
if (data.includes(newContent)) {
console.log(`✅ File already modified with demo credentials: ${filePath}`);
} else if (data.includes(originalContent)) {
fs.writeFileSync(filePath, data.replace(originalContent, newContent), 'utf8');
fs.writeFileSync(
filePath,
data.replace(originalContent, newContent),
'utf8'
);
console.log(`✅ Successfully updated: ${filePath}`);
} else {
console.log(`⚠️ Original content not found in: ${filePath}`);
Expand All @@ -44,6 +48,8 @@ targetFiles.forEach((file) => {
});

if (found === 0) {
console.error('❌ No Login.js / Login.mjs found — admin dist layout may have changed.');
console.error(
'❌ No Login.js / Login.mjs found — admin dist layout may have changed.'
);
process.exit(1);
}
8 changes: 5 additions & 3 deletions strapi/scripts/updateUuid.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
const fs = require('fs');
const path = require('path');
const { v4: uuidv4 } = require('uuid');
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { v4 as uuidv4 } from 'uuid';

// This script will update the UUID of the demo project in order to get some random analytics on
// this demo usage.

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const packageJsonPath = path.join(__dirname, '../package.json');

const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
Expand Down
6 changes: 2 additions & 4 deletions strapi/src/admin/webpack.config.example.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
'use strict';

/* eslint-disable no-unused-vars */
module.exports = (config, webpack) => {
// Note: we provide webpack above so you should not `require` it
export default (config, webpack) => {
// Note: we provide webpack above so you should not import it separately
// Perform customizations to webpack config
// Important: return the modified config
return config;
Expand Down
4 changes: 2 additions & 2 deletions strapi/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"module": "CommonJS",
"moduleResolution": "Node",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2020"],
"target": "ES2019",
"strict": false,
Expand Down