diff --git a/.gitignore b/.gitignore index 7c9ff0d3..aa24bd43 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ examples/templates/dist examples/example#211/font lib package-lock.json +yarn.lock __snapshots__ ### Node ### diff --git a/README.md b/README.md index b151dbdd..a0af06ea 100644 --- a/README.md +++ b/README.md @@ -655,14 +655,14 @@ Define preview web content. Example: #### website.template > Type: `String` -> Default value: [index.ejs](src/website/index.ejs) +> Default value: [index.njk](src/website/index.njk) -Custom template can customize parameters. You can define your own template based on the [default template](src/website/index.ejs). +Custom template can customize parameters. You can define your own template based on the [default template](src/website/index.njk). ```js { website: { - template: path.join(process.cwd(), "my-template.ejs") + template: path.join(process.cwd(), "my-template.njk") } } ``` diff --git a/package.json b/package.json index 893d0a60..1da1507d 100644 --- a/package.json +++ b/package.json @@ -81,15 +81,12 @@ } }, "dependencies": { - "@tsbb/copy-template-dir": "^1.4.0", "auto-config-loader": "^1.7.4", "cheerio": "~1.0.0-rc.12", "colors-cli": "~1.0.28", - "del": "~7.1.0", - "ejs": "~3.1.6", "fs-extra": "~11.2.0", "image2uri": "^2.1.2", - "move-file": "~3.1.0", + "nunjucks": "^3.2.4", "svg2ttf": "~6.0.3", "svgicons2svgfont": "~14.0.0", "svgo": "~3.3.0", @@ -99,8 +96,8 @@ "yargs": "^17.7.2" }, "devDependencies": { - "@types/ejs": "~3.1.0", "@types/fs-extra": "^11.0.1", + "@types/nunjucks": "^3.2.6", "@types/svg2ttf": "~5.0.1", "@types/ttf2eot": "~2.0.0", "@types/ttf2woff": "~2.0.2", diff --git a/src/index.ts b/src/index.ts index ad7d8ebe..2350effb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,3 @@ -/// - import path from 'path'; import { fileURLToPath } from 'url'; import fs from 'fs-extra'; @@ -260,6 +258,7 @@ export default async (options: SvgToFontOptions = {}) => { await fs.ensureDir(options.dist); const unicodeObject = await createSVG(options); + /** @deprecated */ let cssToVars: string[] = []; let cssString: string[] = []; let cssRootVars: string[] = []; @@ -322,14 +321,17 @@ export default async (options: SvgToFontOptions = {}) => { await createSvgSymbol(options); if (options.css) { - const styleTemplatePath = options.styleTemplates || (!options.useNameAsUnicode ? path.resolve(__dirname, 'styles') : path.resolve(__dirname, 'ligature-styles')); - await copyTemplate(styleTemplatePath, options.dist, { + const styleTemplatePath = options.styleTemplates || path.resolve(__dirname, 'styles') + const outDir = typeof options.css === 'object' ? options.css.output || options.dist : options.dist; + await copyTemplate(styleTemplatePath, outDir, { fontname: options.fontName, cssString: cssString.join(''), cssToVars: cssToVars.join(''), + infoData, fontSize: fontSize, timestamp: new Date().getTime(), prefix, + nameAsUnicode: options.useNameAsUnicode, _opts: typeof options.css === 'boolean' ? {} : { ...options.css } }); } @@ -352,7 +354,7 @@ export default async (options: SvgToFontOptions = {}) => { if (name === 'symbol') symbolPath = _path; }); // default template - options.website.template = options.website.template || path.join(__dirname, 'website', 'index.ejs'); + options.website.template = options.website.template || path.join(__dirname, 'website', 'index.njk'); // template data const tempData: SvgToFontOptions['website'] & { fontname: string; @@ -415,6 +417,7 @@ export default async (options: SvgToFontOptions = {}) => { log.log(`${color.green('SUCCESS')} Created React Native Components. `); } + return infoData; } catch (error) { log.log('SvgToFont:CLI:ERR:', error); } diff --git a/src/ligature-styles/_{{filename}}.css b/src/ligature-styles/_{{filename}}.css deleted file mode 100644 index 7b82b1a2..00000000 --- a/src/ligature-styles/_{{filename}}.css +++ /dev/null @@ -1,19 +0,0 @@ -@font-face { - font-family: "{{fontname}}"; - src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}'); /* IE9*/ - src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}#iefix') format('embedded-opentype'), /* IE6-IE8 */ - url("{{cssPath}}{{fontname}}.woff2?t={{timestamp}}") format("woff2"), - url("{{cssPath}}{{fontname}}.woff?t={{timestamp}}") format("woff"), - url('{{cssPath}}{{fontname}}.ttf?t={{timestamp}}') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ - url('{{cssPath}}{{fontname}}.svg?t={{timestamp}}#{{fontname}}') format('svg'); /* iOS 4.1- */ -} - -.{{prefix}} { - font-family: '{{fontname}}' !important;{{fontSize}} - font-style:normal; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -{{cssString}} - diff --git a/src/ligature-styles/_{{filename}}.less b/src/ligature-styles/_{{filename}}.less deleted file mode 100644 index 8307f802..00000000 --- a/src/ligature-styles/_{{filename}}.less +++ /dev/null @@ -1,15 +0,0 @@ -@font-face {font-family: "{{fontname}}"; - src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}'); /* IE9*/ - src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}#iefix') format('embedded-opentype'), /* IE6-IE8 */ - url("{{cssPath}}{{fontname}}.woff2?t={{timestamp}}") format("woff2"), - url("{{cssPath}}{{fontname}}.woff?t={{timestamp}}") format("woff"), - url('{{cssPath}}{{fontname}}.ttf?t={{timestamp}}') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ - url('{{cssPath}}{{fontname}}.svg?t={{timestamp}}#{{fontname}}') format('svg'); /* iOS 4.1- */ -} - -.{{prefix}} { - font-family: '{{fontname}}' !important;{{fontSize}} - font-style:normal; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} diff --git a/src/ligature-styles/_{{filename}}.module.less b/src/ligature-styles/_{{filename}}.module.less deleted file mode 100644 index 8307f802..00000000 --- a/src/ligature-styles/_{{filename}}.module.less +++ /dev/null @@ -1,15 +0,0 @@ -@font-face {font-family: "{{fontname}}"; - src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}'); /* IE9*/ - src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}#iefix') format('embedded-opentype'), /* IE6-IE8 */ - url("{{cssPath}}{{fontname}}.woff2?t={{timestamp}}") format("woff2"), - url("{{cssPath}}{{fontname}}.woff?t={{timestamp}}") format("woff"), - url('{{cssPath}}{{fontname}}.ttf?t={{timestamp}}') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ - url('{{cssPath}}{{fontname}}.svg?t={{timestamp}}#{{fontname}}') format('svg'); /* iOS 4.1- */ -} - -.{{prefix}} { - font-family: '{{fontname}}' !important;{{fontSize}} - font-style:normal; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} diff --git a/src/ligature-styles/_{{filename}}.scss b/src/ligature-styles/_{{filename}}.scss deleted file mode 100644 index 8307f802..00000000 --- a/src/ligature-styles/_{{filename}}.scss +++ /dev/null @@ -1,15 +0,0 @@ -@font-face {font-family: "{{fontname}}"; - src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}'); /* IE9*/ - src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}#iefix') format('embedded-opentype'), /* IE6-IE8 */ - url("{{cssPath}}{{fontname}}.woff2?t={{timestamp}}") format("woff2"), - url("{{cssPath}}{{fontname}}.woff?t={{timestamp}}") format("woff"), - url('{{cssPath}}{{fontname}}.ttf?t={{timestamp}}') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ - url('{{cssPath}}{{fontname}}.svg?t={{timestamp}}#{{fontname}}') format('svg'); /* iOS 4.1- */ -} - -.{{prefix}} { - font-family: '{{fontname}}' !important;{{fontSize}} - font-style:normal; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} diff --git a/src/ligature-styles/_{{filename}}.styl b/src/ligature-styles/_{{filename}}.styl deleted file mode 100644 index 8307f802..00000000 --- a/src/ligature-styles/_{{filename}}.styl +++ /dev/null @@ -1,15 +0,0 @@ -@font-face {font-family: "{{fontname}}"; - src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}'); /* IE9*/ - src: url('{{cssPath}}{{fontname}}.eot?t={{timestamp}}#iefix') format('embedded-opentype'), /* IE6-IE8 */ - url("{{cssPath}}{{fontname}}.woff2?t={{timestamp}}") format("woff2"), - url("{{cssPath}}{{fontname}}.woff?t={{timestamp}}") format("woff"), - url('{{cssPath}}{{fontname}}.ttf?t={{timestamp}}') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ - url('{{cssPath}}{{fontname}}.svg?t={{timestamp}}#{{fontname}}') format('svg'); /* iOS 4.1- */ -} - -.{{prefix}} { - font-family: '{{fontname}}' !important;{{fontSize}} - font-style:normal; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} diff --git a/src/styles/_{{filename}}.css b/src/styles/_{{filename}}.css index f485c178..21246cab 100644 --- a/src/styles/_{{filename}}.css +++ b/src/styles/_{{filename}}.css @@ -8,12 +8,13 @@ url('{{cssPath}}{{fontname}}.svg?t={{timestamp}}#{{fontname}}') format('svg'); /* iOS 4.1- */ } -[class^="{{prefix}}-"], [class*=" {{prefix}}-"] { +{% if nameAsUnicode %}.{{prefix}}{% else %}[class^="{{prefix}}-"], [class*=" {{prefix}}-"]{% endif %} { font-family: '{{fontname}}' !important;{{fontSize}} font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } - +{% if not nameAsUnicode %} {{cssString}} +{% endif %} diff --git a/src/styles/_{{filename}}.less b/src/styles/_{{filename}}.less index bb754b08..1a16d6fa 100644 --- a/src/styles/_{{filename}}.less +++ b/src/styles/_{{filename}}.less @@ -7,11 +7,13 @@ url('{{cssPath}}{{fontname}}.svg?t={{timestamp}}#{{fontname}}') format('svg'); /* iOS 4.1- */ } -[class^="{{prefix}}-"], [class*=" {{prefix}}-"] { +{% if nameAsUnicode %}.{{prefix}}{% else %}[class^="{{prefix}}-"], [class*=" {{prefix}}-"]{% endif %} { font-family: '{{fontname}}' !important;{{fontSize}} font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } -{{cssString}} \ No newline at end of file +{% if not nameAsUnicode %} +{{cssString}} +{% endif %} \ No newline at end of file diff --git a/src/styles/_{{filename}}.module.less b/src/styles/_{{filename}}.module.less index 9f9d1cfe..d3cef6a6 100644 --- a/src/styles/_{{filename}}.module.less +++ b/src/styles/_{{filename}}.module.less @@ -7,13 +7,15 @@ url('{{cssPath}}{{fontname}}.svg?t={{timestamp}}#{{fontname}}') format('svg'); /* iOS 4.1- */ } -[class^="{{prefix}}-"], [class*=" {{prefix}}-"] { +{% if nameAsUnicode %}.{{prefix}}{% else %}[class^="{{prefix}}-"], [class*=" {{prefix}}-"]{% endif %} { font-family: '{{fontname}}' !important;{{fontSize}} font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } +{% if not nameAsUnicode %} :global { {{cssString}} -} \ No newline at end of file +} +{% endif %} \ No newline at end of file diff --git a/src/styles/_{{filename}}.scss b/src/styles/_{{filename}}.scss index 626afe83..cba3161e 100644 --- a/src/styles/_{{filename}}.scss +++ b/src/styles/_{{filename}}.scss @@ -7,12 +7,16 @@ url('{{cssPath}}{{fontname}}.svg?t={{timestamp}}#{{fontname}}') format('svg'); /* iOS 4.1- */ } -[class^="{{prefix}}-"], [class*=" {{prefix}}-"] { +{% if nameAsUnicode %}.{{prefix}}{% else %}[class^="{{prefix}}-"], [class*=" {{prefix}}-"]{% endif %} { font-family: '{{fontname}}' !important;{{fontSize}} font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } -{{cssString}} -{{cssToVars}} +{% if not nameAsUnicode %} +{{ cssString }} +{% for name, value in infoData %} +${{prefix}}-{{ name }}: '{{ value.encodedCode }}'; +{%- endfor %} +{% endif %} \ No newline at end of file diff --git a/src/styles/_{{filename}}.styl b/src/styles/_{{filename}}.styl index bb754b08..0e503f63 100644 --- a/src/styles/_{{filename}}.styl +++ b/src/styles/_{{filename}}.styl @@ -7,11 +7,16 @@ url('{{cssPath}}{{fontname}}.svg?t={{timestamp}}#{{fontname}}') format('svg'); /* iOS 4.1- */ } -[class^="{{prefix}}-"], [class*=" {{prefix}}-"] { +{% if nameAsUnicode %}.{{prefix}}{% else %}[class^="{{prefix}}-"], [class*=" {{prefix}}-"]{% endif %} { font-family: '{{fontname}}' !important;{{fontSize}} font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } -{{cssString}} \ No newline at end of file +{% if not nameAsUnicode %} +{{ cssString }} +{% for name, value in infoData %} +${{prefix}}-{{ name }} = '{{ value.encodedCode }}' +{%- endfor %} +{% endif %} \ No newline at end of file diff --git a/src/types/index.d.ts b/src/types/index.d.ts deleted file mode 100644 index d748de76..00000000 --- a/src/types/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/// - -declare module '@tsbb/copy-template-dir' { - export default function(templateDir: string, targetDir: string, options: Record, callback: (err: Error, createdFiles: string[]) => void): void; -} diff --git a/src/utils.ts b/src/utils.ts index bd574c4a..e60e8c2f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,16 +1,13 @@ import { SVGIcons2SVGFontStream } from 'svgicons2svgfont'; import fs, { ReadStream } from 'fs-extra'; import path from 'path'; -import ejs from 'ejs'; import color from 'colors-cli'; import { load } from 'cheerio'; import svg2ttf from 'svg2ttf'; import ttf2eot from 'ttf2eot'; import ttf2woff from 'ttf2woff'; import ttf2woff2 from 'ttf2woff2'; -import copy from '@tsbb/copy-template-dir'; -import { deleteAsync } from 'del'; -import { moveFile } from 'move-file'; +import nunjucks from 'nunjucks'; import { type SvgToFontOptions } from './'; import { log } from './log.js'; @@ -38,20 +35,20 @@ export function createSVG(options: SvgToFontOptions = {}): Promise path.basename(svgPath, path.extname(svgPath))); await fs.writeFile( DIST_PATH, - `export enum ${enumName} {\n` + - fileNames.map(name => ` ${snakeToUppercase(name)} = "${options.classNamePrefix}-${name}"`).join(',\n') + - '\n}\n\n' + - `export type ${enumName}Classname = ${fileNames.map(name => `"${options.classNamePrefix}-${name}"`).join(' | ')}\n` + - `export type ${enumName}Icon = ${fileNames.map(name => `"${name}"`).join(' | ')}\n` + - `export const ${enumName}Prefix = "${options.classNamePrefix}-"` + [ + `export enum ${enumName} {`, + ...fileNames.map(name => ` ${snakeToUppercase(name)} = "${options.classNamePrefix}-${name}",`), + '}', + `export type ${enumName}Classname = ${fileNames.map(name => `"${options.classNamePrefix}-${name}"`).join(' | ')}`, + `export type ${enumName}Icon = ${fileNames.map(name => `"${name}"`).join(' | ')}`, + `export const ${enumName}Prefix = "${options.classNamePrefix}-"`, + ].join('\n'), ); log.log(`${color.green('SUCCESS')} Created ${DIST_PATH}`); } @@ -282,60 +281,48 @@ export type CSSOptions = { templateVars?: Record; } +// As we are processing css files, we need to eacape HTML entities. +const safeNunjucks = nunjucks.configure({ autoescape: false }); + /** * Copy template files */ -export function copyTemplate(inDir: string, outDir: string, { _opts, ...vars }: Record & { _opts: CSSOptions}) { - const removeFiles: Array = []; - return new Promise((resolve, reject) => { - copy(inDir, outDir, { - ...(_opts.templateVars || {}), - ...vars, - cssPath: _opts.cssPath || '', - filename: _opts.fileName || vars.fontname, - }, async (err, createdFiles) => { - if (err) reject(err); - createdFiles = createdFiles.map(filePath => { - if (_opts.include && (new RegExp(_opts.include)).test(filePath) || !_opts.include) { - return filePath; - } else { - removeFiles.push(filePath); - } - }).filter(Boolean); - if (removeFiles.length > 0) { - await deleteAsync([...removeFiles]); - } - createdFiles = await Promise.all(createdFiles.map(async (file) => { - if (!file.endsWith('.template')) { - return file; - } - - const changedFile = file.replace('.template', ''); - await moveFile(file, changedFile); - return changedFile; - })); - if (_opts.output) { - const output = path.join(process.cwd(), _opts.output); - await Promise.all(createdFiles.map(async (file) => { - await moveFile(file, path.join(output, path.basename(file))); - return null; - })); - } - createdFiles.forEach(filePath => log.log(`${color.green('SUCCESS')} Created ${filePath} `)); - resolve(createdFiles); - }) - }); +export async function copyTemplate(inDir: string, outDir: string, { _opts, ...vars }: Record & { _opts: CSSOptions }) { + const files = await fs.readdir(inDir, { withFileTypes: true }); + const context = { + ...(_opts.templateVars || {}), + ...vars, + cssPath: _opts.cssPath || '', + filename: _opts.fileName || vars.fontname, + } + await fs.ensureDir(outDir); + for (const file of files) { + if (!file.isFile()) continue; + if (_opts.include && !(new RegExp(_opts.include)).test(file.name)) continue; + let newFileName = file.name.replace(/\.template$/, '').replace(/^_/, ''); + for (const key in context) newFileName = newFileName.replace(`{{${key}}}`, `${context[key]}`); + const template = await fs.readFile(path.join(inDir, file.name), 'utf8'); + const content = safeNunjucks.renderString(template, context); + const filePath = path.join(outDir, newFileName) + await fs.writeFile(filePath, content); + log.log(`${color.green('SUCCESS')} Created ${filePath} `); + } }; /** * Create HTML */ -export function createHTML(outPath: string,data: ejs.Data, options?: ejs.Options): Promise { - return new Promise((resolve, reject) => { - ejs.renderFile(outPath, data, options, (err, str) => { - if (err) reject(err); - resolve(str); - }); +export function createHTML(templatePath: string, data: Record): string { + return nunjucks.renderString(fs.readFileSync(templatePath, 'utf8'), { + ...data, + Date: Date, + JSON: JSON, + Math: Math, + Number: Number, + Object: Object, + RegExp: RegExp, + String: String, + typeof: (v: any) => typeof v, }); }; diff --git a/src/website/index.ejs b/src/website/index.njk similarity index 69% rename from src/website/index.ejs rename to src/website/index.njk index 5d9b8b8c..f610d88d 100644 --- a/src/website/index.ejs +++ b/src/website/index.njk @@ -2,17 +2,17 @@ - <%=_title%> + {{ _title }} - <%if (meta && Object.keys(meta).length > 0) { Object.keys(meta).forEach((metaName) => { %> - - <%})}%> - <%if(favicon) {%> - - <%}%> - <%if(_type === 'font-class' && _link) {%> - - <%}%> + {% for k, v in meta|default({}) %} + + {% endfor %} + {% if favicon %} + + {% endif %} + {% if _type === 'font-class' and _link %} + + {% endif %} - <%if (corners && corners.url) {%> - - <%}%> - <%if(corners && corners.url) {%> - - + {% if corners and corners.url %} + + - <%}%> + {% endif %} - <%if(typeof logo==='string' ) {%> + {% if typeof(logo) === 'string' %} - <%-logo%> + {{ logo }} - <%}%> - <%=_title%><%-version%> + {% endif %} + {{ _title }}{{ version }} - <%-description || (meta && meta.description)%> + {{ description|default(meta.description) }} - <%if(links && links.length > 0) { links.forEach((linkItem, index) => {%> - <%-linkItem.title%><%if(links.length-1 !== index){%> · <%}%> - <%})}%> + {% for linkItem in links|default([]) %} + {{ linkItem.title }}{% if not loop.last %} · {% endif %} + {% endfor %} - <%-_IconHtml%> + {{ _IconHtml|safe }} - <%if(links && links.length > 0) { links.forEach((linkItem, index) => {%> - <%-linkItem.title%><%if(links.length-1 !== index){%> · <%}%> - <%})}%> + {% for linkItem in links|default([]) %} + {{ linkItem.title }}{% if not loop.last %} · {% endif %} + {% endfor %} diff --git a/test/index.test.js b/test/index.test.js index caa80958..6755cc17 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -19,7 +19,6 @@ it('example test case.', async () => { 'index.html', 'react', 'reactNative', - 'styles', 'svgtofont.css', 'svgtofont.d.ts', 'svgtofont.eot', @@ -41,7 +40,6 @@ it('example simple test case.', async () => { const dist = path.resolve(process.cwd(), 'examples', 'example', 'example'); const fileNames = await fs.readdir(dist); expect(fileNames).toEqual([ - 'styles', 'svgtofont.css', 'svgtofont.eot', 'svgtofont.less', @@ -64,7 +62,6 @@ it('templates templates test case.', async () => { 'index.html', 'react', 'reactNative', - 'styles', 'svgtofont.css', 'svgtofont.eot', 'svgtofont.json',
- <%if(links && links.length > 0) { links.forEach((linkItem, index) => {%> - <%-linkItem.title%><%if(links.length-1 !== index){%> · <%}%> - <%})}%> + {% for linkItem in links|default([]) %} + {{ linkItem.title }}{% if not loop.last %} · {% endif %} + {% endfor %}