Skip to content

Commit

Permalink
ci: refactor build code
Browse files Browse the repository at this point in the history
  • Loading branch information
simonguo committed Oct 16, 2024
1 parent 4743436 commit 1a0f774
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 154 deletions.
2 changes: 1 addition & 1 deletion gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const rename = require('gulp-rename');
const gulp = require('gulp');
const swc = require('gulp-swc');
const generateIconComponents = require('./scripts/generateIconComponents');
const { default: proxyDirectories } = require('./scripts/proxyDirectories');
const proxyDirectories = require('./scripts/proxyDirectories');
const lessDir = './src/less';
const tsSrcFiles = ['./src/**/*.tsx', './src/**/*.ts', '!./src/**/*.d.ts'];
const libRoot = path.join(__dirname, './lib');
Expand Down
134 changes: 56 additions & 78 deletions scripts/generateIconComponents.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,24 @@ const path = require('path');
const lowerCase = require('lodash/lowerCase');

const SRC_DIR = '../node_modules/@rsuite/icon-font/lib';

/**
* Dist directory
* @type {string}
*/
const DIST_DIR = '../src/icons';
const ALIAS_DIR = '../';

const resolvePath = (...paths) => path.resolve(__dirname, ...paths);

module.exports = function() {
const files = glob.sync(resolvePath(`${SRC_DIR}/**/*.js`));
const icons = [];
const legacyIcons = [];
files.forEach(function generateComponent(svgPath, index) {
const basename = path.basename(svgPath);
const componentName = path.basename(basename, path.extname(basename));
const categoryName = path.relative(path.resolve(__dirname, SRC_DIR), path.dirname(svgPath));
const isLegacy = categoryName === 'legacy';
isLegacy ? legacyIcons.push(componentName) : icons.push(componentName);
console.log(`(${index + 1}/${files.length}) Generating ${componentName}.tsx ...`);
/**
* Writes the component content to a file.
* @param {string} componentName - The name of the component.
* @param {string} categoryName - The category of the component.
* @param {boolean} isLegacy - Whether the component is in the legacy category.
*/
const writeComponentFile = (componentName, categoryName, isLegacy) => {
const componentPath = resolvePath(DIST_DIR, isLegacy ? 'legacy' : '', `${componentName}.tsx`);
const relativeSvgImport = isLegacy ? '../../createSvgIcon' : '../createSvgIcon';
const svgImportPath = `@rsuite/icon-font/lib/${categoryName}/${componentName}`;

// Output Icons src
fs.outputFileSync(
resolvePath(DIST_DIR, isLegacy ? 'legacy' : '', `${componentName}.tsx`),
`// Generated by script, don't edit it please.
import createSvgIcon from '${isLegacy ? '../' : ''}../createSvgIcon';
import ${componentName}Svg from '@rsuite/icon-font/lib/${categoryName}/${componentName}';
const content = `// Generated by script, don't edit it please.
import createSvgIcon from '${relativeSvgImport}';
import ${componentName}Svg from '${svgImportPath}';
const ${componentName} = createSvgIcon({
as: ${componentName}Svg,
Expand All @@ -41,67 +31,55 @@ const ${componentName} = createSvgIcon({
});
export default ${componentName};
`
);
`;

// It only release when publish
if (process.env.PUBLISH) {
// Output Icons alias
fs.outputFileSync(
resolvePath(ALIAS_DIR, isLegacy ? 'legacy' : '', `${componentName}.js`),
`"use strict";
fs.outputFileSync(componentPath, content);
};

Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "default", {
enumerable: true,
get: function get() {
return _${componentName}["default"];
}
});
/**
* Writes the index.ts or legacy/index.ts file with the export statements for all components.
* @param {string[]} componentNames - List of component names.
* @param {boolean} isLegacy - Whether to write the legacy index file.
*/
const writeIndexFile = (componentNames, isLegacy = false) => {
const content = componentNames
.map(name => `export { default as ${name} } from './${name}';`)
.join('\n');

var _${componentName} = _interopRequireDefault(require(".${isLegacy ? '.' : ''}/lib/icons${
isLegacy ? '/legacy' : ''
}/${componentName}"));
const indexPath = resolvePath(DIST_DIR, isLegacy ? 'legacy/index.ts' : 'index.ts');
const header = `// Generated by script, don't edit it please.\n`;

function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
`
);
fs.outputFileSync(indexPath, header + content + '\n');
};

// Output Icons alias define
fs.outputFileSync(
resolvePath(ALIAS_DIR, isLegacy ? 'legacy' : '', `${componentName}.d.ts`),
`/// <reference types="react" />
declare const ${componentName}: import("react").FC<import(".${
isLegacy ? '.' : ''
}/lib/Icon").IconProps>;
export default ${componentName};
`
);
}
});
/**
* Main function to generate all icon components and index files.
*/
module.exports = function generateIconComponents() {
const files = glob.sync(resolvePath(`${SRC_DIR}/**/*.js`));
const icons = [];
const legacyIcons = [];

const iconsContent = icons
.map(componentName => `export { default as ${componentName} } from './${componentName}';`)
.join('\n');
files.forEach((svgPath, index) => {
const basename = path.basename(svgPath);
const componentName = path.basename(basename, path.extname(basename));
const categoryName = path.relative(resolvePath(SRC_DIR), path.dirname(svgPath));
const isLegacy = categoryName === 'legacy';

const legacyIconsContent = legacyIcons
.map(componentName => `export { default as ${componentName} } from './${componentName}';`)
.join('\n');
isLegacy ? legacyIcons.push(componentName) : icons.push(componentName);
console.log(`(${index + 1}/${files.length}) Generating ${componentName}.tsx ...`);

fs.outputFileSync(
resolvePath(DIST_DIR, 'index.ts'),
`// Generated by script, don't edit it please.
${iconsContent}
`
);
fs.outputFileSync(
resolvePath(DIST_DIR, 'legacy/index.ts'),
`// Generated by script, don't edit it please.
${legacyIconsContent}
`
);
try {
writeComponentFile(componentName, categoryName, isLegacy);
} catch (error) {
console.error(`Error generating ${componentName}:`, error);
}
});

try {
writeIndexFile(icons);
writeIndexFile(legacyIcons, true);
} catch (error) {
console.error('Error writing index files:', error);
}
};
185 changes: 110 additions & 75 deletions scripts/proxyDirectories.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
/**
* Create a package.json for each directory and proxy to CJS and ESM files.
* Can make importing a component easier.
*
*/

const path = require('path');
const fs = require('fs');
const util = require('util');
Expand All @@ -13,91 +7,132 @@ const writeFile = util.promisify(fs.writeFile);
const srcRoot = path.join(__dirname, '../src');
const libRoot = path.join(__dirname, '../lib');

function findResources(options) {
const { dir = srcRoot, ignores = [], isFile } = options;
const resources = [];
fs.readdirSync(dir).forEach(item => {
/**
* Recursively finds resources in a directory, with optional filters for files and ignored directories.
* @param {Object} options - The options for finding resources.
* @param {string} options.dir - The directory to search in.
* @param {string[]} options.ignores - List of directories to ignore.
* @param {boolean} options.isFile - Whether to include files in the search results.
* @returns {string[]} - List of found resources.
*/
function findResources({ dir = srcRoot, ignores = [], isFile }) {
const resources = fs.readdirSync(dir).map(item => {
const itemPath = path.resolve(dir, item);
const pathname = itemPath.replace(/[a-z0-9\-]*\//gi, '').replace('.tsx', '');

if (fs.statSync(itemPath).isDirectory()) {
resources.push(pathname);
}
if (isFile && fs.statSync(itemPath).isFile()) {
resources.push(pathname);
const isDirectory = fs.statSync(itemPath).isDirectory();
const isFileMatch = isFile && fs.statSync(itemPath).isFile();

if (isDirectory || isFileMatch) {
return pathname;
}
return null;
});

//console.log(resources);

return resources.filter(item => !ignores.includes(item));
return resources.filter(item => item && !ignores.includes(item));
}

function proxyResource(options) {
const { pkgName = '@rsuite/icons', name, file, filePath = '../', subPath = '/' } = options;
const proxyPkg = {
name: `${pkgName}/${name}`,
private: true,
main: `${filePath}/cjs${subPath}${file}.js`,
module: `${filePath}/esm${subPath}${file}.js`,
types: `${filePath}/esm${subPath}${file}.d.ts`
};

return JSON.stringify(proxyPkg, null, 2) + '\n';
/**
* Creates a proxy package.json content.
* @param {Object} options - The options for the proxy.
* @param {string} options.pkgName - The base package name.
* @param {string} options.name - The name of the resource.
* @param {string} options.file - The file name without extension.
* @param {string} [options.filePath='../'] - The relative path to the resource.
* @param {string} [options.subPath='/'] - The subpath inside the package.
* @returns {string} - The package.json content.
*/
function proxyResource({ pkgName = '@rsuite/icons', name, file, filePath = '../', subPath = '/' }) {
return (
JSON.stringify(
{
name: `${pkgName}/${name}`,
private: true,
main: `${filePath}/cjs${subPath}${file}.js`,
module: `${filePath}/esm${subPath}${file}.js`,
types: `${filePath}/esm${subPath}${file}.d.ts`
},
null,
2
) + '\n'
);
}

async function writePkgFile(options) {
const {
resources = [],
pkgName = '@rsuite/icons',
dir = libRoot,
subPath,
filePath = '..'
} = options;
/**
* Writes a package.json file for each resource in the specified directory.
* @param {Object} options - The options for writing the package file.
* @param {string[]} options.resources - The list of resources.
* @param {string} options.pkgName - The base package name.
* @param {string} options.dir - The directory where package.json will be written.
* @param {string} options.subPath - The subpath for the resource.
* @param {string} options.filePath - The file path to the resource.
*/
async function writePkgFile({
resources = [],
pkgName = '@rsuite/icons',
dir = libRoot,
subPath,
filePath = '..'
}) {
await Promise.all(
resources.map(async item => {
const name = item;
const file = `${item}`;
const proxyDir = path.join(dir, name);
await mkDir(dir).catch(() => {});
await mkDir(proxyDir).catch(() => {});
await writeFile(
`${proxyDir}/package.json`,
proxyResource({ pkgName, name, file, filePath, subPath })
).catch(err => {
if (err) console.error(err.toString());
});
try {
const name = item;
const file = item;
const proxyDir = path.join(dir, name);

await mkDir(proxyDir, { recursive: true }); // Ensure directory creation is recursive
await writeFile(
`${proxyDir}/package.json`,
proxyResource({ pkgName, name, file, filePath, subPath })
);
} catch (error) {
console.error(`Error writing package.json for ${item}:`, error);
}
})
);
}

/**
* Main function to proxy components.
*/
async function proxyComponent() {
const icons = findResources({
dir: path.join(srcRoot, 'icons'),
isFile: true,
ignores: ['legacy']
});

await writePkgFile({ resources: icons, subPath: '/icons/' });

const legacyIcons = findResources({ dir: path.join(srcRoot, 'icons/legacy'), isFile: true });

await writePkgFile({
resources: legacyIcons,
pkgName: '@rsuite/icons/legacy',
subPath: '/icons/legacy/',
filePath: '../..',
dir: path.join(libRoot, 'legacy')
});

const components = ['Icon', 'IconProvider', 'createIconFont', 'createSvgIcon'];

await writePkgFile({ resources: components });
}

async function proxy() {
await proxyComponent();
try {
const icons = findResources({
dir: path.join(srcRoot, 'icons'),
isFile: true,
ignores: ['legacy']
});

await writePkgFile({ resources: icons, subPath: '/icons/' });

const legacyIcons = findResources({
dir: path.join(srcRoot, 'icons/legacy'),
isFile: true
});

await writePkgFile({
resources: legacyIcons,
pkgName: '@rsuite/icons/legacy',
subPath: '/icons/legacy/',
filePath: '../..',
dir: path.join(libRoot, 'legacy')
});

const components = ['Icon', 'IconProvider', 'createIconFont', 'createSvgIcon'];
await writePkgFile({ resources: components });
} catch (error) {
console.error('Error in proxyComponent:', error);
}
}

module.exports.findResources = findResources;
module.exports.default = proxy;
/**
* Entry point function to trigger the proxying process.
*/
module.exports = async function proxy() {
try {
await proxyComponent();
} catch (error) {
console.error('Error in proxy:', error);
}
};

0 comments on commit 1a0f774

Please sign in to comment.