@cloud-ru/ft-gulp-icon-builder 1.0.1-preview-df9ff74.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE +201 -0
  3. package/README.md +4 -0
  4. package/dist/cjs/index.d.ts +7 -0
  5. package/dist/cjs/index.js +30 -0
  6. package/dist/cjs/svg-create-sprite.d.ts +4 -0
  7. package/dist/cjs/svg-create-sprite.js +41 -0
  8. package/dist/cjs/svg-fixer.d.ts +1 -0
  9. package/dist/cjs/svg-fixer.js +29 -0
  10. package/dist/cjs/svg-index-file.d.ts +6 -0
  11. package/dist/cjs/svg-index-file.js +62 -0
  12. package/dist/cjs/svg-size-validator.d.ts +1 -0
  13. package/dist/cjs/svg-size-validator.js +27 -0
  14. package/dist/cjs/svg-svgr.d.ts +5 -0
  15. package/dist/cjs/svg-svgr.js +26 -0
  16. package/dist/cjs/svg-uniq-validator.d.ts +1 -0
  17. package/dist/cjs/svg-uniq-validator.js +19 -0
  18. package/dist/cjs/utils/createPipeTransformer.d.ts +10 -0
  19. package/dist/cjs/utils/createPipeTransformer.js +35 -0
  20. package/dist/cjs/utils/index-template.d.ts +1 -0
  21. package/dist/cjs/utils/index-template.js +34 -0
  22. package/dist/cjs/utils/index.d.ts +3 -0
  23. package/dist/cjs/utils/index.js +22 -0
  24. package/dist/cjs/utils/name.d.ts +2 -0
  25. package/dist/cjs/utils/name.js +15 -0
  26. package/dist/cjs/utils/svgr-template.d.ts +7 -0
  27. package/dist/cjs/utils/svgr-template.js +51 -0
  28. package/dist/esm/index.d.ts +7 -0
  29. package/dist/esm/index.js +7 -0
  30. package/dist/esm/svg-create-sprite.d.ts +4 -0
  31. package/dist/esm/svg-create-sprite.js +35 -0
  32. package/dist/esm/svg-fixer.d.ts +1 -0
  33. package/dist/esm/svg-fixer.js +23 -0
  34. package/dist/esm/svg-index-file.d.ts +6 -0
  35. package/dist/esm/svg-index-file.js +56 -0
  36. package/dist/esm/svg-size-validator.d.ts +1 -0
  37. package/dist/esm/svg-size-validator.js +24 -0
  38. package/dist/esm/svg-svgr.d.ts +5 -0
  39. package/dist/esm/svg-svgr.js +23 -0
  40. package/dist/esm/svg-uniq-validator.d.ts +1 -0
  41. package/dist/esm/svg-uniq-validator.js +16 -0
  42. package/dist/esm/utils/createPipeTransformer.d.ts +10 -0
  43. package/dist/esm/utils/createPipeTransformer.js +32 -0
  44. package/dist/esm/utils/index-template.d.ts +1 -0
  45. package/dist/esm/utils/index-template.js +30 -0
  46. package/dist/esm/utils/index.d.ts +3 -0
  47. package/dist/esm/utils/index.js +3 -0
  48. package/dist/esm/utils/name.d.ts +2 -0
  49. package/dist/esm/utils/name.js +8 -0
  50. package/dist/esm/utils/svgr-template.d.ts +7 -0
  51. package/dist/esm/utils/svgr-template.js +47 -0
  52. package/package.json +50 -0
  53. package/src/index.ts +7 -0
  54. package/src/svg-create-sprite.ts +33 -0
  55. package/src/svg-fixer.ts +19 -0
  56. package/src/svg-index-file.ts +63 -0
  57. package/src/svg-size-validator.ts +30 -0
  58. package/src/svg-svgr.ts +38 -0
  59. package/src/svg-uniq-validator.ts +21 -0
  60. package/src/utils/createPipeTransformer.ts +36 -0
  61. package/src/utils/index-template.ts +35 -0
  62. package/src/utils/index.ts +3 -0
  63. package/src/utils/name.ts +10 -0
  64. package/src/utils/svgr-template.ts +58 -0
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getTemplate = void 0;
4
+ const size = 24;
5
+ const getTemplate = ({ idPrefix, generateDataTestId }) => ({ imports, interfaces, componentName, exports }, { tpl }) => {
6
+ const testId = generateDataTestId(componentName);
7
+ const componentProp = size ? `{ size = ${size}, ...props }: ISvgIconProps` : `{ size, ...props }: ISvgIconProps`;
8
+ return tpl `
9
+ // DO NOT EDIT IT MANUALLY
10
+ ${imports}
11
+ ${interfaces}
12
+
13
+ export interface ISvgIconProps extends SVGProps<SVGSVGElement> {
14
+ className?: string;
15
+ size?: number;
16
+ style?: React.CSSProperties;
17
+ }
18
+
19
+ const ${componentName} = React.forwardRef((${componentProp}, ref: React.Ref<SVGSVGElement>) => {
20
+ props.width = undefined;
21
+ props.height = undefined;
22
+
23
+ const testId = ${JSON.stringify(testId)};
24
+ const isCustomSize = typeof size === 'number';
25
+
26
+ if(isCustomSize) {
27
+ if(!props.style) props.style = {};
28
+ props.style.width = size+"px";
29
+ props.style.height = size+"px";
30
+ }
31
+
32
+ return (
33
+ <svg
34
+ ref={ref}
35
+ xmlns='http://www.w3.org/2000/svg'
36
+ width={24}
37
+ height={24}
38
+ fill='currentColor'
39
+ viewBox='0 0 24 24'
40
+ data-test-id={'icon-' + testId}
41
+ {...props}
42
+ >
43
+ <use href={'#' + ${JSON.stringify(idPrefix)} + '-' + testId} />
44
+ </svg>
45
+ );
46
+ });
47
+
48
+ ${exports}
49
+ `;
50
+ };
51
+ exports.getTemplate = getTemplate;
@@ -0,0 +1,7 @@
1
+ export { gulpFixSvg } from './svg-fixer';
2
+ export { gulpSvgSizeValidator } from './svg-size-validator';
3
+ export { gulpSvgUniqValidator } from './svg-uniq-validator';
4
+ export { gulpCreateSvgSprite } from './svg-create-sprite';
5
+ export { gulpSvgr, type GulpSvgrParams } from './svg-svgr';
6
+ export { gulpSvgIndexFile } from './svg-index-file';
7
+ export * from './utils';
@@ -0,0 +1,7 @@
1
+ export { gulpFixSvg } from './svg-fixer';
2
+ export { gulpSvgSizeValidator } from './svg-size-validator';
3
+ export { gulpSvgUniqValidator } from './svg-uniq-validator';
4
+ export { gulpCreateSvgSprite } from './svg-create-sprite';
5
+ export { gulpSvgr } from './svg-svgr';
6
+ export { gulpSvgIndexFile } from './svg-index-file';
7
+ export * from './utils';
@@ -0,0 +1,4 @@
1
+ export declare function gulpCreateSvgSprite({ filePath, idPrefix }: {
2
+ idPrefix: string;
3
+ filePath: string;
4
+ }): import("stream").Transform;
@@ -0,0 +1,35 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
13
+ // @ts-ignore
14
+ import SVGSprite from 'svg-sprite';
15
+ import { createPipeTransformer } from './utils/createPipeTransformer';
16
+ export function gulpCreateSvgSprite({ filePath, idPrefix }) {
17
+ const sprite = new SVGSprite({
18
+ dest: path.resolve(process.cwd(), filePath),
19
+ mode: {
20
+ symbol: true,
21
+ },
22
+ });
23
+ return createPipeTransformer({
24
+ transformer: (file, _encoding, callback) => {
25
+ const content = file.contents.toString();
26
+ const name = (idPrefix + '-' + path.basename(file.path)).split('.')[0];
27
+ sprite.add(name, null, content.replace(/fill="[A-Za-z0-9#]+"/g, 'fill="inherit"'));
28
+ callback(null, file);
29
+ },
30
+ onEnd: () => __awaiter(this, void 0, void 0, function* () {
31
+ const { result } = yield sprite.compileAsync();
32
+ fs.writeFileSync(path.resolve(process.cwd(), filePath), result.symbol.sprite.contents.toString());
33
+ }),
34
+ });
35
+ }
@@ -0,0 +1 @@
1
+ export declare function gulpFixSvg(): import("stream").Transform;
@@ -0,0 +1,23 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
11
+ // @ts-expect-error
12
+ import svgFixer from 'oslllo-svg-fixer';
13
+ import { createPipeTransformer } from './utils/createPipeTransformer';
14
+ const fixSvg = svgFixer.fixString;
15
+ export function gulpFixSvg() {
16
+ return createPipeTransformer({
17
+ transformer: (file, encoding, callback) => __awaiter(this, void 0, void 0, function* () {
18
+ const fixedSvg = yield fixSvg(file.contents);
19
+ file.contents = Buffer.from(fixedSvg, encoding);
20
+ return callback(null, file);
21
+ }),
22
+ });
23
+ }
@@ -0,0 +1,6 @@
1
+ type Params = {
2
+ src: string;
3
+ dest: string;
4
+ };
5
+ export declare function gulpSvgIndexFile({ src, dest }: Params): import("stream").Transform;
6
+ export {};
@@ -0,0 +1,56 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import { createPipeTransformer } from './utils/createPipeTransformer';
13
+ import { getComponent } from './utils/index-template';
14
+ const capitalizeFirstLetter = (str) => {
15
+ if (!str)
16
+ return str;
17
+ return str.charAt(0).toUpperCase() + str.slice(1);
18
+ };
19
+ export function gulpSvgIndexFile({ src, dest }) {
20
+ const folders = {};
21
+ const getTail = (fullPath) => fullPath.replace(`${path.resolve(process.cwd(), src)}/`, '');
22
+ return createPipeTransformer({
23
+ transformer: (file, _encoding, callback) => {
24
+ const folder = file.dirname;
25
+ const files = folders[folder] || [];
26
+ files.push(file.basename);
27
+ folders[folder] = files;
28
+ callback(null, file);
29
+ },
30
+ onEnd: () => __awaiter(this, void 0, void 0, function* () {
31
+ const imports = [];
32
+ let indexFile;
33
+ for (const [folder, files] of Object.entries(folders)) {
34
+ if (files.length !== 1 && files.length !== 2) {
35
+ throw new Error(`Unexpected count of files in folder: ${folder}`);
36
+ }
37
+ {
38
+ // index file for icon folder
39
+ const component = files.length === 2 ? getComponent(files, 'Component') : `export * from './${files[0].split('.')[0]}';`;
40
+ const finalFilePath = path.resolve(dest, getTail(folder), 'index.tsx');
41
+ fs.writeFileSync(finalFilePath, component);
42
+ }
43
+ {
44
+ // index file for all
45
+ const folderName = path.basename(folder);
46
+ const componentName = capitalizeFirstLetter(folderName);
47
+ imports.push(`export { default as ${componentName} } from './${folderName}';`);
48
+ if (!indexFile) {
49
+ indexFile = path.resolve(dest, getTail(path.dirname(folder)), 'index.tsx');
50
+ }
51
+ }
52
+ }
53
+ fs.writeFileSync(indexFile, imports.join('\n'));
54
+ }),
55
+ });
56
+ }
@@ -0,0 +1 @@
1
+ export declare function gulpSvgSizeValidator(maxSize?: number): import("stream").Transform;
@@ -0,0 +1,24 @@
1
+ import { XMLParser } from 'fast-xml-parser';
2
+ import { createPipeTransformer } from './utils/createPipeTransformer';
3
+ export function gulpSvgSizeValidator(maxSize = 24) {
4
+ const parser = new XMLParser({
5
+ ignoreAttributes: false,
6
+ attributeNamePrefix: '@_',
7
+ });
8
+ return createPipeTransformer({
9
+ transformer: (file, _encoding, callback) => {
10
+ const content = file.contents.toString();
11
+ /*
12
+ А че, если мы не будем здесь стопать парсингом xml пайп, а вместо этого отложим парсинг и проверку в асинхронный пул?
13
+ А дожидаться разгребания этого пула будем в колбеке закрытия стрима, когда вся основная работа уже сделана.
14
+ */
15
+ const xml = parser.parse(content);
16
+ const width = xml.svg['@_width'];
17
+ const height = xml.svg['@_height'];
18
+ if ((width && Number(width) > maxSize) || (height && Number(height) > maxSize)) {
19
+ return callback(new Error(`Icon size is bigger than ${maxSize}px, please make it smaller:\n\t${file.path}\n`));
20
+ }
21
+ return callback(null, file);
22
+ },
23
+ });
24
+ }
@@ -0,0 +1,5 @@
1
+ import type { Template } from '@svgr/babel-plugin-transform-svg-component';
2
+ export type GulpSvgrParams = {
3
+ template: Template;
4
+ };
5
+ export declare function gulpSvgr({ template }: GulpSvgrParams): import("stream").Transform;
@@ -0,0 +1,23 @@
1
+ import { transform } from '@svgr/core';
2
+ import { getComponentName } from './utils';
3
+ import { createPipeTransformer } from './utils/createPipeTransformer';
4
+ export function gulpSvgr({ template }) {
5
+ return createPipeTransformer({
6
+ transformer: (file, _encoding, callback) => {
7
+ const content = file.contents.toString();
8
+ const componentName = getComponentName(file.basename);
9
+ const result = transform.sync(content, {
10
+ icon: true,
11
+ template,
12
+ typescript: true,
13
+ expandProps: 'end',
14
+ jsxRuntime: 'classic',
15
+ exportType: 'default',
16
+ plugins: ['@svgr/plugin-jsx'],
17
+ }, { componentName, caller: { name: 'gulp-svgr' }, filePath: file.path });
18
+ file.contents = Buffer.from(result);
19
+ file.extname = '.tsx';
20
+ callback(null, file);
21
+ },
22
+ });
23
+ }
@@ -0,0 +1 @@
1
+ export declare function gulpSvgUniqValidator(): import("stream").Transform;
@@ -0,0 +1,16 @@
1
+ import { createPipeTransformer } from './utils/createPipeTransformer';
2
+ export function gulpSvgUniqValidator() {
3
+ const storage = new Map();
4
+ return createPipeTransformer({
5
+ transformer: (file, _encoding, callback) => {
6
+ const content = file.contents.toString();
7
+ const alreadyHaveIcon = storage.get(content);
8
+ const iconPath = file.path;
9
+ if (alreadyHaveIcon) {
10
+ return callback(new Error(`There are duplicating icons:\n\t${alreadyHaveIcon} \n\t${iconPath}\n`));
11
+ }
12
+ storage.set(content, iconPath);
13
+ callback(null, file);
14
+ },
15
+ });
16
+ }
@@ -0,0 +1,10 @@
1
+ import { Transform } from 'node:stream';
2
+ import type File from 'vinyl';
3
+ type TransformCallback = (err?: Error | null, data?: File) => void;
4
+ type Transformer = (file: File.BufferFile, _encoding: BufferEncoding, callback: TransformCallback) => void;
5
+ export type PipeTransformerParams = {
6
+ transformer: Transformer;
7
+ onEnd?: () => void;
8
+ };
9
+ export declare function createPipeTransformer({ transformer, onEnd }: PipeTransformerParams): Transform;
10
+ export {};
@@ -0,0 +1,32 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { Transform } from 'node:stream';
11
+ export function createPipeTransformer({ transformer, onEnd }) {
12
+ const stream = new Transform({
13
+ objectMode: true,
14
+ transform(file, _encoding, callback) {
15
+ return __awaiter(this, void 0, void 0, function* () {
16
+ if (file.isNull()) {
17
+ return callback(null, file);
18
+ }
19
+ if (file.isStream()) {
20
+ return callback(new Error('Streaming not supported'));
21
+ }
22
+ if (file.isBuffer()) {
23
+ return transformer(file, _encoding, callback);
24
+ }
25
+ return callback(null, file);
26
+ });
27
+ },
28
+ });
29
+ if (onEnd)
30
+ stream.on('end', onEnd);
31
+ return stream;
32
+ }
@@ -0,0 +1 @@
1
+ export declare const getComponent: (iconFiles: string[], componentName: string) => string;
@@ -0,0 +1,30 @@
1
+ const ICON_PROPS = `
2
+ export interface ISvgIconProps extends SVGProps<SVGSVGElement> {
3
+ className?: string;
4
+ size?: number;
5
+ style?: React.CSSProperties;
6
+ }
7
+ `;
8
+ const REACT_IMPORT = "import { SVGProps, forwardRef, Ref } from 'react';";
9
+ const DEFAULT_SIZE = 24;
10
+ const getExportName = (fileName) => (fileName.endsWith('xs.tsx') ? 'XsSVG' : 'SSVG');
11
+ const withoutExtension = (fileName) => fileName.split('.')[0];
12
+ export const getComponent = (iconFiles, componentName) => {
13
+ const iconImports = iconFiles
14
+ .map(file => `import { default as ${getExportName(file)} } from './${withoutExtension(file)}';`)
15
+ .join('\n');
16
+ const finalComponentName = `${componentName}SVG`;
17
+ return `
18
+ ${REACT_IMPORT}
19
+ ${iconImports}
20
+
21
+ ${ICON_PROPS}
22
+
23
+ const ${finalComponentName} = forwardRef(({ size = ${DEFAULT_SIZE}, ...props }: ISvgIconProps, ref: Ref<SVGSVGElement>) => {
24
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
25
+ // @ts-ignore
26
+ return Number(size) >= 20 ? <SSVG ref={ref} size={size} {...props} /> : <XsSVG ref={ref} size={size} {...props} />;
27
+ });
28
+
29
+ export default ${finalComponentName};`;
30
+ };
@@ -0,0 +1,3 @@
1
+ export { getTemplate } from './svgr-template';
2
+ export { getComponent } from './index-template';
3
+ export * from './name';
@@ -0,0 +1,3 @@
1
+ export { getTemplate } from './svgr-template';
2
+ export { getComponent } from './index-template';
3
+ export * from './name';
@@ -0,0 +1,2 @@
1
+ export declare function getTestId(componentName: string): string;
2
+ export declare function getComponentName(fileName: string): string;
@@ -0,0 +1,8 @@
1
+ import camelcase from 'camelcase';
2
+ import kebabCase from 'kebab-case';
3
+ export function getTestId(componentName) {
4
+ return kebabCase(componentName.replace('Svg', ''), false);
5
+ }
6
+ export function getComponentName(fileName) {
7
+ return camelcase(fileName);
8
+ }
@@ -0,0 +1,7 @@
1
+ import type { Template } from '@svgr/babel-plugin-transform-svg-component';
2
+ type getTemplateParams = {
3
+ idPrefix: string;
4
+ generateDataTestId: (fileName: string) => string;
5
+ };
6
+ export declare const getTemplate: ({ idPrefix, generateDataTestId }: getTemplateParams) => Template;
7
+ export {};
@@ -0,0 +1,47 @@
1
+ const size = 24;
2
+ export const getTemplate = ({ idPrefix, generateDataTestId }) => ({ imports, interfaces, componentName, exports }, { tpl }) => {
3
+ const testId = generateDataTestId(componentName);
4
+ const componentProp = size ? `{ size = ${size}, ...props }: ISvgIconProps` : `{ size, ...props }: ISvgIconProps`;
5
+ return tpl `
6
+ // DO NOT EDIT IT MANUALLY
7
+ ${imports}
8
+ ${interfaces}
9
+
10
+ export interface ISvgIconProps extends SVGProps<SVGSVGElement> {
11
+ className?: string;
12
+ size?: number;
13
+ style?: React.CSSProperties;
14
+ }
15
+
16
+ const ${componentName} = React.forwardRef((${componentProp}, ref: React.Ref<SVGSVGElement>) => {
17
+ props.width = undefined;
18
+ props.height = undefined;
19
+
20
+ const testId = ${JSON.stringify(testId)};
21
+ const isCustomSize = typeof size === 'number';
22
+
23
+ if(isCustomSize) {
24
+ if(!props.style) props.style = {};
25
+ props.style.width = size+"px";
26
+ props.style.height = size+"px";
27
+ }
28
+
29
+ return (
30
+ <svg
31
+ ref={ref}
32
+ xmlns='http://www.w3.org/2000/svg'
33
+ width={24}
34
+ height={24}
35
+ fill='currentColor'
36
+ viewBox='0 0 24 24'
37
+ data-test-id={'icon-' + testId}
38
+ {...props}
39
+ >
40
+ <use href={'#' + ${JSON.stringify(idPrefix)} + '-' + testId} />
41
+ </svg>
42
+ );
43
+ });
44
+
45
+ ${exports}
46
+ `;
47
+ };
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@cloud-ru/ft-gulp-icon-builder",
3
+ "version": "1.0.1-preview-df9ff74.0",
4
+ "description": "",
5
+ "keywords": [
6
+ ""
7
+ ],
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "scripts": {
12
+ "gulp": "gulp"
13
+ },
14
+ "author": "yetihead <yetibrain@yandex.ru>",
15
+ "license": "Apache-2.0",
16
+ "types": "./dist/esm/index.d.ts",
17
+ "exports": {
18
+ "import": "./dist/esm/index.js",
19
+ "require": "./dist/cjs/index.js"
20
+ },
21
+ "files": [
22
+ "dist/esm",
23
+ "dist/cjs",
24
+ "src",
25
+ "./CHANGELOG.md",
26
+ "LICENSE"
27
+ ],
28
+ "homepage": "https://github.com/cloud-ru-tech/frontend-toolspackages/gulp-icon-builder",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/cloud-ru-tech/frontend-tools.git",
32
+ "directory": "packages/gulp-icon-builder"
33
+ },
34
+ "dependencies": {
35
+ "@svgr/core": "8.1.0",
36
+ "@svgr/plugin-jsx": "8.1.0",
37
+ "camelcase": "5.3.1",
38
+ "fast-xml-parser": "5.3.3",
39
+ "gulp": "5.0.1",
40
+ "kebab-case": "2.0.2",
41
+ "oslllo-svg-fixer": "6.0.1",
42
+ "svg-sprite": "2.0.4",
43
+ "vinyl": "3.0.1"
44
+ },
45
+ "devDependencies": {
46
+ "@types/vinyl": "2.0.12",
47
+ "gulp-cli": "3.1.0"
48
+ },
49
+ "gitHead": "c9d8d521a1fede651fad5e57d618849e24b61acb"
50
+ }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export { gulpFixSvg } from './svg-fixer';
2
+ export { gulpSvgSizeValidator } from './svg-size-validator';
3
+ export { gulpSvgUniqValidator } from './svg-uniq-validator';
4
+ export { gulpCreateSvgSprite } from './svg-create-sprite';
5
+ export { gulpSvgr, type GulpSvgrParams } from './svg-svgr';
6
+ export { gulpSvgIndexFile } from './svg-index-file';
7
+ export * from './utils';
@@ -0,0 +1,33 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
4
+ // @ts-ignore
5
+ import SVGSprite from 'svg-sprite';
6
+
7
+ import { createPipeTransformer } from './utils/createPipeTransformer';
8
+
9
+ export function gulpCreateSvgSprite({ filePath, idPrefix }: { idPrefix: string; filePath: string }) {
10
+ const sprite = new SVGSprite({
11
+ dest: path.resolve(process.cwd(), filePath),
12
+ mode: {
13
+ symbol: true,
14
+ },
15
+ });
16
+
17
+ return createPipeTransformer({
18
+ transformer: (file, _encoding, callback) => {
19
+ const content = file.contents.toString();
20
+
21
+ const name = (idPrefix + '-' + path.basename(file.path)).split('.')[0];
22
+
23
+ sprite.add(name, null, content.replace(/fill="[A-Za-z0-9#]+"/g, 'fill="inherit"'));
24
+
25
+ callback(null, file);
26
+ },
27
+
28
+ onEnd: async () => {
29
+ const { result } = await sprite.compileAsync();
30
+ fs.writeFileSync(path.resolve(process.cwd(), filePath), result.symbol.sprite.contents.toString());
31
+ },
32
+ });
33
+ }
@@ -0,0 +1,19 @@
1
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2
+ // @ts-expect-error
3
+ import svgFixer from 'oslllo-svg-fixer';
4
+
5
+ import { createPipeTransformer } from './utils/createPipeTransformer';
6
+
7
+ type SvgFixer = { fixString: (svg: string | Buffer) => Promise<string> };
8
+
9
+ const fixSvg = (svgFixer as unknown as SvgFixer).fixString;
10
+
11
+ export function gulpFixSvg() {
12
+ return createPipeTransformer({
13
+ transformer: async (file, encoding, callback) => {
14
+ const fixedSvg = await fixSvg(file.contents);
15
+ file.contents = Buffer.from(fixedSvg, encoding);
16
+ return callback(null, file);
17
+ },
18
+ });
19
+ }
@@ -0,0 +1,63 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ import { createPipeTransformer } from './utils/createPipeTransformer';
5
+ import { getComponent } from './utils/index-template';
6
+
7
+ type Params = {
8
+ src: string;
9
+ dest: string;
10
+ };
11
+
12
+ const capitalizeFirstLetter = (str: string): string => {
13
+ if (!str) return str;
14
+ return str.charAt(0).toUpperCase() + str.slice(1);
15
+ };
16
+
17
+ export function gulpSvgIndexFile({ src, dest }: Params) {
18
+ const folders: Record<string, string[]> = {};
19
+
20
+ const getTail = (fullPath: string) => fullPath.replace(`${path.resolve(process.cwd(), src)}/`, '');
21
+
22
+ return createPipeTransformer({
23
+ transformer: (file, _encoding, callback) => {
24
+ const folder = file.dirname;
25
+ const files = folders[folder] || [];
26
+ files.push(file.basename);
27
+ folders[folder] = files;
28
+
29
+ callback(null, file);
30
+ },
31
+
32
+ onEnd: async () => {
33
+ const imports: string[] = [];
34
+ let indexFile: string | undefined;
35
+
36
+ for (const [folder, files] of Object.entries(folders)) {
37
+ if (files.length !== 1 && files.length !== 2) {
38
+ throw new Error(`Unexpected count of files in folder: ${folder}`);
39
+ }
40
+
41
+ {
42
+ // index file for icon folder
43
+ const component =
44
+ files.length === 2 ? getComponent(files, 'Component') : `export * from './${files[0].split('.')[0]}';`;
45
+ const finalFilePath = path.resolve(dest, getTail(folder), 'index.tsx');
46
+ fs.writeFileSync(finalFilePath, component);
47
+ }
48
+
49
+ {
50
+ // index file for all
51
+ const folderName = path.basename(folder);
52
+ const componentName = capitalizeFirstLetter(folderName);
53
+ imports.push(`export { default as ${componentName} } from './${folderName}';`);
54
+ if (!indexFile) {
55
+ indexFile = path.resolve(dest, getTail(path.dirname(folder)), 'index.tsx');
56
+ }
57
+ }
58
+ }
59
+
60
+ fs.writeFileSync(indexFile as string, imports.join('\n'));
61
+ },
62
+ });
63
+ }
@@ -0,0 +1,30 @@
1
+ import { XMLParser } from 'fast-xml-parser';
2
+
3
+ import { createPipeTransformer } from './utils/createPipeTransformer';
4
+
5
+ export function gulpSvgSizeValidator(maxSize: number = 24) {
6
+ const parser = new XMLParser({
7
+ ignoreAttributes: false,
8
+ attributeNamePrefix: '@_',
9
+ });
10
+
11
+ return createPipeTransformer({
12
+ transformer: (file, _encoding, callback) => {
13
+ const content = file.contents.toString();
14
+ /*
15
+ А че, если мы не будем здесь стопать парсингом xml пайп, а вместо этого отложим парсинг и проверку в асинхронный пул?
16
+ А дожидаться разгребания этого пула будем в колбеке закрытия стрима, когда вся основная работа уже сделана.
17
+ */
18
+ const xml = parser.parse(content);
19
+
20
+ const width = xml.svg['@_width'];
21
+ const height = xml.svg['@_height'];
22
+
23
+ if ((width && Number(width) > maxSize) || (height && Number(height) > maxSize)) {
24
+ return callback(new Error(`Icon size is bigger than ${maxSize}px, please make it smaller:\n\t${file.path}\n`));
25
+ }
26
+
27
+ return callback(null, file);
28
+ },
29
+ });
30
+ }