diff --git a/.changeset/config.json b/.changeset/config.json index 71ec94f9..eac60f62 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,5 +7,5 @@ "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": [] + "ignore": ["figma-to-swiftui", "tsconfig", "nextjs"] } diff --git a/.changeset/rotten-trains-greet.md b/.changeset/rotten-trains-greet.md new file mode 100644 index 00000000..4c3a252f --- /dev/null +++ b/.changeset/rotten-trains-greet.md @@ -0,0 +1,6 @@ +--- +"svg-to-swiftui-core": patch +"figma-to-swiftui": patch +--- + +Added usage comment prefix diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 7e169c7a..7ab83380 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -31,5 +31,5 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} name: codecov-umbrella - fail_ci_if_error: true + fail_ci_if_error: false verbose: true diff --git a/packages/figma-to-swiftui/README.md b/packages/figma-to-swiftui/README.md new file mode 100644 index 00000000..64eed2ce --- /dev/null +++ b/packages/figma-to-swiftui/README.md @@ -0,0 +1,26 @@ +# Figma to SwiftUI Converter + +## Overview + +This plugin lets you export paths from Figma to SwiftUI Shape structure. + +## How to use + +- Select the vector object that you want to convert. +- Make sure you **select the specific vector path** that you want to convert. +- Open Figma DEV mode. +- Select SwiftUI Code Generator from the language export options. +- Copy code and paste it into your SwiftUI project! + +## Links + +- Visit Web App version of this converter [here](https://dub.sh/svg-to-swiftui). +- Need help with developing your designs? Visit my development agency [Quassum](https://dub.sh/quassum)! +- Made by [Antoni](https://dub.sh/antoni) + +## Community + +Wanna suggest some changes or just to say hi? Join our community here: + +- [Github Discussions](https://github.com/quassum/SVG-to-SwiftUI/discussions) +- [Quassum Discord](https://discord.gg/VzpZ7np622) diff --git a/packages/figma-to-swiftui/package.json b/packages/figma-to-swiftui/package.json index ba6249c4..cc56686f 100644 --- a/packages/figma-to-swiftui/package.json +++ b/packages/figma-to-swiftui/package.json @@ -5,8 +5,6 @@ "scripts": { "build": "tsup src/index.ts --format iife --target es5", "clean": "rimraf dist", - "test": "jest", - "coverage": "jest --ci --coverage", "lint": "eslint ./src/**/*", "lint:fix": "eslint ./src/**/* --fix", "format": "prettier --write ./src --loglevel error" diff --git a/packages/figma-to-swiftui/src/index.ts b/packages/figma-to-swiftui/src/index.ts index 0f76f28c..10bd88bb 100644 --- a/packages/figma-to-swiftui/src/index.ts +++ b/packages/figma-to-swiftui/src/index.ts @@ -29,6 +29,7 @@ if (figma.editorType === "dev" && figma.mode === "codegen") { const swiftUI = convert(SVG_TEMPLATE, { structName: node.name.replace(/\s/g, ""), + usageCommentPrefix: true, }); return [ diff --git a/packages/svg-to-swiftui-core/src/index.ts b/packages/svg-to-swiftui-core/src/index.ts index c189ba5b..664fe1b5 100644 --- a/packages/svg-to-swiftui-core/src/index.ts +++ b/packages/svg-to-swiftui-core/src/index.ts @@ -1,12 +1,16 @@ -import { ElementNode, parse } from "svg-parser"; -import { generateSwiftUIShape } from "./stubs"; -import { SwiftUIGeneratorConfig, TranspilerOptions } from "./types"; +import {ElementNode, parse} from 'svg-parser'; +import {SwiftUIGeneratorConfig, TranspilerOptions} from './types'; -import { handleElement } from "./elementHandlers"; -import { extractSVGProperties, getSVGElement } from "./utils"; -import { DEFAULT_CONFIG } from "./constants"; +import {handleElement} from './elementHandlers'; +import {extractSVGProperties, getSVGElement} from './utils'; +import {DEFAULT_CONFIG} from './constants'; +import { + createFunctionTemplate, + createStructTemplate, + createUsageCommentTemplate, +} from './templates'; -export * from "./types"; +export * from './types'; /** * This function converts SVG string into SwiftUI @@ -15,18 +19,18 @@ export * from "./types"; * @param config Optional configuration object. */ export function convert( - rawSVGString: string, - config?: SwiftUIGeneratorConfig, + rawSVGString: string, + config?: SwiftUIGeneratorConfig ): string { - const AST = parse(rawSVGString); - const svgElement = getSVGElement(AST); - if (svgElement) { - return swiftUIGenerator(svgElement, config); - } else { - throw new Error( - "Could not find SVG element, please provide full SVG source!", - ); - } + const AST = parse(rawSVGString); + const svgElement = getSVGElement(AST); + if (svgElement) { + return swiftUIGenerator(svgElement, config); + } else { + throw new Error( + 'Could not find SVG element, please provide full SVG source!' + ); + } } /** @@ -35,29 +39,56 @@ export function convert( * @param config Optional configuration object. */ function swiftUIGenerator( - svgElement: ElementNode, - config?: SwiftUIGeneratorConfig, + svgElement: ElementNode, + config?: SwiftUIGeneratorConfig ): string { - const svgProperties = extractSVGProperties(svgElement); - - // The initial options passed to the first element. - const rootTranspilerOptions: TranspilerOptions = { - ...svgProperties, - precision: config?.precision || 10, - lastPathId: 0, - indentationSize: config?.indentationSize || 4, - currentIndentationLevel: 0, - parentStyle: {}, - }; - - // Generate SwiftUI Shape body. - const generatedBody = handleElement(svgElement, rootTranspilerOptions); - - // Inject generated body into the Shape struct template. - const fullSwiftUIShape = generateSwiftUIShape(generatedBody, { - ...DEFAULT_CONFIG, - ...config, - }); - - return fullSwiftUIShape; + const svgProperties = extractSVGProperties(svgElement); + + // The initial options passed to the first element. + const rootTranspilerOptions: TranspilerOptions = { + ...svgProperties, + precision: config?.precision || 10, + lastPathId: 0, + indentationSize: config?.indentationSize || 4, + currentIndentationLevel: 0, + parentStyle: {}, + }; + + const configWithDefaults = { + ...DEFAULT_CONFIG, + ...config, + }; + + // Generate SwiftUI Shape body. + const generatedBody = handleElement(svgElement, rootTranspilerOptions); + + const fullSwiftUIShape = createStructTemplate({ + name: configWithDefaults.structName!, + indent: configWithDefaults.indentationSize, + returnType: 'Shape', + body: createFunctionTemplate({ + name: 'path', + parameters: [['in rect', 'CGRect']], + returnType: 'Path', + indent: configWithDefaults.indentationSize, + body: [ + 'var path = Path()', + 'let width = rect.size.width', + 'let height = rect.size.height', + ...generatedBody, + 'return path', + ], + }), + }); + + if (config?.usageCommentPrefix) { + const usageComment = createUsageCommentTemplate({ + config, + viewBox: svgProperties.viewBox, + }); + + return [...usageComment, '', ...fullSwiftUIShape].join('\n'); + } + + return fullSwiftUIShape.join('\n'); } diff --git a/packages/svg-to-swiftui-core/src/stubs.ts b/packages/svg-to-swiftui-core/src/stubs.ts deleted file mode 100644 index cf84582c..00000000 --- a/packages/svg-to-swiftui-core/src/stubs.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {SwiftUIGeneratorConfig} from './types'; - -export const generateSwiftUIShape = ( - body: string[], - config: SwiftUIGeneratorConfig -) => { - const indStr = new Array(config.indentationSize).fill(' ').join(''); - - const getInd = (indLevel: number) => - new Array(indLevel).fill(indStr).join(''); - - const indentedBody = `${getInd(2)}${body.join(`\n${getInd(2)}`)}`; - - return [ - `struct ${config.structName!}: Shape {`, - `${getInd(1)}func path(in rect: CGRect) -> Path {`, - `${getInd(2)}var path = Path()`, - `${getInd(2)}let width = rect.size.width`, - `${getInd(2)}let height = rect.size.height`, - indentedBody, - `${getInd(2)}return path`, - `${getInd(1)}}`, - '}', - ].join('\n'); -}; diff --git a/packages/svg-to-swiftui-core/src/templates.ts b/packages/svg-to-swiftui-core/src/templates.ts new file mode 100644 index 00000000..75790572 --- /dev/null +++ b/packages/svg-to-swiftui-core/src/templates.ts @@ -0,0 +1,63 @@ +import {SwiftUIGeneratorConfig, ViewBoxData} from './types'; + +export const createUsageCommentTemplate = ({ + config, + viewBox, +}: { + config: SwiftUIGeneratorConfig; + viewBox: ViewBoxData; +}) => [ + '// To use this shape, just add it to your SwiftUI View:', + `// ${config.structName}().fill().frame(width: ${viewBox.width}, height: ${viewBox.height})`, +]; + +type ParameterName = string; +type ParameterType = string; + +const indentStrings = ({indent, body}: {indent: number; body: string[]}) => { + return body.map(row => `${new Array(indent).fill(' ').join('')}${row}`); +}; + +const parametersToStrings = (parameters: [ParameterName, ParameterType][]) => { + return parameters.map(([name, type]) => `${name}: ${type}`).join(', '); +}; + +export const createFunctionTemplate = ({ + name, + parameters, + returnType, + body, + indent = 4, +}: { + name: string; + parameters: [ParameterName, ParameterType][]; + returnType: string; + body: string[]; + indent?: number; +}) => { + const parametersString = parametersToStrings(parameters); + + return [ + `func ${name}(${parametersString}) -> ${returnType} {`, + ...indentStrings({body, indent}), + `}`, + ]; +}; + +export const createStructTemplate = ({ + name, + returnType, + body, + indent = 4, +}: { + name: string; + returnType: string; + body: string[]; + indent?: number; +}) => { + return [ + `struct ${name}: ${returnType} {`, + ...indentStrings({body, indent}), + `}`, + ]; +}; diff --git a/packages/svg-to-swiftui-core/src/tests/index.test.ts b/packages/svg-to-swiftui-core/src/tests/index.test.ts index bce4b8de..e5762984 100644 --- a/packages/svg-to-swiftui-core/src/tests/index.test.ts +++ b/packages/svg-to-swiftui-core/src/tests/index.test.ts @@ -13,10 +13,13 @@ test('conversion-1', () => { test('convert-circle', () => { const rawSVG = loadContentFile('circle.svg'); const expectedResult = loadContentFile('circle.swift'); + const result = convert(rawSVG, { precision: 2, structName: 'CircleShape', }); + console.log('expectedResult', expectedResult); + console.log('result', result); expect(result).toBe(expectedResult); }); diff --git a/packages/svg-to-swiftui-core/src/types.ts b/packages/svg-to-swiftui-core/src/types.ts index 964e734a..7a433bde 100644 --- a/packages/svg-to-swiftui-core/src/types.ts +++ b/packages/svg-to-swiftui-core/src/types.ts @@ -2,6 +2,7 @@ export interface SwiftUIGeneratorConfig { structName?: string; precision?: number; indentationSize?: number; + usageCommentPrefix?: boolean; } export interface TranspilerOptions {