Skip to content

Add default exports file to generate script (#1) #133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
200 changes: 119 additions & 81 deletions scripts/generate.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@
const fs = require('fs');
const { sync: mkdirp } = require('mkdirp');
const path = require('path');
const rollup = require('rollup');
const babel = require('rollup-plugin-babel');
const fs = require('fs')
const { sync: mkdirp } = require('mkdirp')
const path = require('path')
const rollup = require('rollup')
const babel = require('rollup-plugin-babel')

const svgPathRegex = /<path\s([^>]*)>/g;
const svgAttrRegex = /(?:\s*|^)([^= ]*)="([^"]*)"/g;
const validIconName = /^[A-Z]/;
const svgPathRegex = /<path\s([^>]*)>/g
const svgAttrRegex = /(?:\s*|^)([^= ]*)="([^"]*)"/g
const validIconName = /^[A-Z]/

function getRollupInputConfig(target) {
return {
external: [target],
plugins: [
babel({
presets: [
['es2015', { modules: false }],
target
],
plugins: [
'transform-object-rest-spread',
'external-helpers'
]
presets: [['es2015', { modules: false }], target],
plugins: ['transform-object-rest-spread', 'external-helpers']
})
]
};
}
}

function normalizeName(name) {
return name.split(/[ -]/g).map(part => {
return part.charAt(0).toUpperCase() + part.slice(1);
}).join('') + 'Icon';
return (
name
.split(/[ -]/g)
.map((part) => {
return part.charAt(0).toUpperCase() + part.slice(1)
})
.join('') + 'Icon'
)
}

function checkAllowedAttr(attr, value, content, name) {
Expand All @@ -56,20 +55,20 @@ function checkAllowedAttr(attr, value, content, name) {
function extractPath(content, name) {
const allPaths = []
while (true) {
const svgPathMatches = svgPathRegex.exec(content);
const svgPath = svgPathMatches && svgPathMatches[1];
const svgPathMatches = svgPathRegex.exec(content)
const svgPath = svgPathMatches && svgPathMatches[1]
if (!svgPath) {
break
}
const attrs = {}
while (true) {
const svgAttrMatches = svgAttrRegex.exec(svgPath);
const svgAttrMatches = svgAttrRegex.exec(svgPath)
if (!svgAttrMatches) {
break
}
if (!checkAllowedAttr(svgAttrMatches[1], svgAttrMatches[2])) {
throw new Error(
`Unknown SVG attr in ${name}: ${svgAttrMatches[1]}="${svgAttrMatches[2]}"\n${content}`,
`Unknown SVG attr in ${name}: ${svgAttrMatches[1]}="${svgAttrMatches[2]}"\n${content}`
)
}
attrs[svgAttrMatches[1]] = svgAttrMatches[2]
Expand All @@ -82,112 +81,151 @@ function extractPath(content, name) {
if (allPaths.length !== 1 || !allPaths[0].d) {
throw new Error(
`Wrong number of path in ${name}: ${allPaths.length}\n` +
`${JSON.stringify(allPaths, undefined, 2)}\n${content}`,
`${JSON.stringify(allPaths, undefined, 2)}\n${content}`
)
}
return allPaths[0].d
}

function collectComponents(svgFilesPath) {
const svgFiles = fs.readdirSync(svgFilesPath);
const svgFiles = fs.readdirSync(svgFilesPath)

const icons = [];
const icons = []
for (const svgFile of svgFiles) {
const svgFilePath = path.join(svgFilesPath, svgFile);
const svgFilePath = path.join(svgFilesPath, svgFile)

// Handle sub-directories.
const stats = fs.statSync(svgFilePath);
const stats = fs.statSync(svgFilePath)
if (stats.isDirectory()) {
icons.push(...collectComponents(svgFilePath));
continue;
icons.push(...collectComponents(svgFilePath))
continue
}

const origName = svgFile.slice(0, -4);
const name = normalizeName(origName);
const origName = svgFile.slice(0, -4)
const name = normalizeName(origName)

if (!validIconName.exec(name)) {
console.log(`Skipping icon with invalid name: ${svgFilePath}`)
continue;
continue
}

const content = fs.readFileSync(svgFilePath);
const content = fs.readFileSync(svgFilePath)
let svgPath
try {
svgPath = extractPath(content, svgFilePath);
svgPath = extractPath(content, svgFilePath)
} catch (err) {
// Ignore file.
console.log(err)
continue;
continue
}

const icon = {
name: name,
fileName: name + '.js',
defFileName: name + '.d.ts',
svgPath
};
}

icons.push(icon);
icons.push(icon)
}

return icons;
return icons
}

async function generate(target, jsCb, tsCb, tsAllCb) {
const basePath = path.resolve(__dirname, '..');
const svgFilesPath = path.resolve(basePath, 'node_modules/remixicon/icons');
const buildPath = path.resolve(basePath, 'build');
mkdirp(buildPath);
const publishPath = path.resolve(basePath, 'publish-' + target);
mkdirp(publishPath);
const distPath = path.resolve(publishPath, 'dist');
mkdirp(distPath);

console.log('Collecting components...');
const components = collectComponents(svgFilesPath);
console.log('Generating components...');
const pathsToUnlink = [];
const basePath = path.resolve(__dirname, '..')
const svgFilesPath = path.resolve(basePath, 'node_modules/remixicon/icons')
const buildPath = path.resolve(basePath, 'build')
mkdirp(buildPath)
const publishPath = path.resolve(basePath, 'publish-' + target)
mkdirp(publishPath)
const distPath = path.resolve(publishPath, 'dist')
mkdirp(distPath)

console.log('Collecting components...')
const components = collectComponents(svgFilesPath)
console.log('Generating components...')
const pathsToUnlink = []
const componentNames = []
for (const [index, component] of components.entries()) {
if (!component.aliasFor) {
console.log(`Generating ${component.name}... (${index + 1}/${components.length})`);
} else {
console.log(`Generating alias ${component.name}... (${index + 1}/${components.length})`);
}
try {
/*
if (!component.aliasFor) {
console.log(
`Generating ${component.name}... (${index + 1}/${components.length})`
)
} else {
console.log(
`Generating alias ${component.name}... (${index + 1}/${
components.length
})`
)
}
*/

const fileContent = jsCb(component);
const inputPath = path.resolve(buildPath, component.fileName);
const outputPath = path.resolve(publishPath, component.fileName);
const fileContent = jsCb(component)

fs.writeFileSync(inputPath, fileContent);
const inputPath = path.resolve(buildPath, component.fileName)
const outputPath = path.resolve(publishPath, component.fileName)

const bundle = await rollup.rollup({
input: inputPath,
...getRollupInputConfig(target)
});
fs.writeFileSync(inputPath, fileContent)

await bundle.write({
file: outputPath,
format: 'cjs'
});
const bundle = await rollup.rollup({
input: inputPath,
...getRollupInputConfig(target)
})

// remember paths to unlink later
if (!pathsToUnlink.includes(inputPath)) {
pathsToUnlink.push(inputPath);
}
await bundle.write({
file: outputPath,
format: 'cjs',
exports: 'auto'
})

const definitionContent = tsCb(component);
fs.writeFileSync(path.join(publishPath, component.defFileName), definitionContent);
// remember paths to unlink later
if (!pathsToUnlink.includes(inputPath)) {
pathsToUnlink.push(inputPath)
}

const definitionContent = tsCb(component)
fs.writeFileSync(
path.join(publishPath, component.defFileName),
definitionContent
)

componentNames.push(component.name)
} catch (error) {
console.log(`→ ${error}`)
}
}

console.log('Generating typings...');
console.log('Generating index.ts')

const defaultImports = []
componentNames.forEach((name) => {
defaultImports.push(`import ${name} from './${name}'`)
})
const exportDefaultImport = []
componentNames.forEach((name) => {
exportDefaultImport.push(name)
})

fs.writeFileSync(
path.resolve(publishPath, 'index.ts'),
`${defaultImports.join('\n')}\n
export default {
${exportDefaultImport.join(',\n ')}
}`
)

console.log('Generating typings...')
// create the global typings.d.ts
const typingsContent = tsAllCb();
fs.writeFileSync(path.resolve(distPath, 'typings.d.ts'), typingsContent);
const typingsContent = tsAllCb()
fs.writeFileSync(path.resolve(distPath, 'typings.d.ts'), typingsContent)

// clean up
for (const pathToUnlink of pathsToUnlink) {
fs.unlinkSync(pathToUnlink);
fs.unlinkSync(pathToUnlink)
}
}

module.exports = generate;
module.exports = generate