@figma/code-connect 0.1.2 → 0.2.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 (106) hide show
  1. package/README.md +123 -25
  2. package/dist/cli.js +7 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/connect.d.ts +15 -0
  5. package/dist/commands/connect.d.ts.map +1 -1
  6. package/dist/commands/connect.js +137 -76
  7. package/dist/commands/connect.js.map +1 -1
  8. package/dist/common/__test__/project.test.d.ts +2 -0
  9. package/dist/common/__test__/project.test.d.ts.map +1 -0
  10. package/dist/common/__test__/project.test.js +14 -0
  11. package/dist/common/__test__/project.test.js.map +1 -0
  12. package/dist/common/api.d.ts +56 -2
  13. package/dist/common/api.d.ts.map +1 -1
  14. package/dist/common/external.d.ts +6 -3
  15. package/dist/common/external.d.ts.map +1 -1
  16. package/dist/common/external.js +13 -1
  17. package/dist/common/external.js.map +1 -1
  18. package/dist/common/figma_connect.d.ts +11 -9
  19. package/dist/common/figma_connect.d.ts.map +1 -1
  20. package/dist/common/intrinsics.d.ts +38 -6
  21. package/dist/common/intrinsics.d.ts.map +1 -1
  22. package/dist/common/intrinsics.js +160 -26
  23. package/dist/common/intrinsics.js.map +1 -1
  24. package/dist/common/logging.d.ts +9 -0
  25. package/dist/common/logging.d.ts.map +1 -1
  26. package/dist/common/logging.js +21 -1
  27. package/dist/common/logging.js.map +1 -1
  28. package/dist/common/updates.d.ts +4 -0
  29. package/dist/common/updates.d.ts.map +1 -0
  30. package/dist/common/updates.js +87 -0
  31. package/dist/common/updates.js.map +1 -0
  32. package/dist/connect/create.d.ts +5 -1
  33. package/dist/connect/create.d.ts.map +1 -1
  34. package/dist/connect/create.js +55 -69
  35. package/dist/connect/create.js.map +1 -1
  36. package/dist/connect/delete_docs.d.ts.map +1 -1
  37. package/dist/connect/delete_docs.js +1 -4
  38. package/dist/connect/delete_docs.js.map +1 -1
  39. package/dist/connect/figma_rest_api.d.ts +5 -0
  40. package/dist/connect/figma_rest_api.d.ts.map +1 -1
  41. package/dist/connect/figma_rest_api.js +10 -1
  42. package/dist/connect/figma_rest_api.js.map +1 -1
  43. package/dist/connect/helpers.d.ts +1 -1
  44. package/dist/connect/helpers.d.ts.map +1 -1
  45. package/dist/connect/helpers.js +6 -5
  46. package/dist/connect/helpers.js.map +1 -1
  47. package/dist/connect/parser_executable_types.d.ts +298 -0
  48. package/dist/connect/parser_executable_types.d.ts.map +1 -0
  49. package/dist/connect/parser_executable_types.js +108 -0
  50. package/dist/connect/parser_executable_types.js.map +1 -0
  51. package/dist/connect/parser_executables.d.ts +8 -0
  52. package/dist/connect/parser_executables.d.ts.map +1 -0
  53. package/dist/connect/parser_executables.js +132 -0
  54. package/dist/connect/parser_executables.js.map +1 -0
  55. package/dist/connect/project.d.ts +137 -0
  56. package/dist/connect/project.d.ts.map +1 -0
  57. package/dist/connect/project.js +411 -0
  58. package/dist/connect/project.js.map +1 -0
  59. package/dist/connect/upload.d.ts.map +1 -1
  60. package/dist/connect/upload.js +1 -4
  61. package/dist/connect/upload.js.map +1 -1
  62. package/dist/connect/validation.d.ts +6 -1
  63. package/dist/connect/validation.d.ts.map +1 -1
  64. package/dist/connect/validation.js +12 -12
  65. package/dist/connect/validation.js.map +1 -1
  66. package/dist/connect/wizard/helpers.d.ts +7 -0
  67. package/dist/connect/wizard/helpers.d.ts.map +1 -0
  68. package/dist/connect/wizard/helpers.js +55 -0
  69. package/dist/connect/wizard/helpers.js.map +1 -0
  70. package/dist/connect/wizard/run_wizard.d.ts +3 -0
  71. package/dist/connect/wizard/run_wizard.d.ts.map +1 -0
  72. package/dist/connect/wizard/run_wizard.js +479 -0
  73. package/dist/connect/wizard/run_wizard.js.map +1 -0
  74. package/dist/parser_scripts/get_file_if_exists.d.ts +2 -0
  75. package/dist/parser_scripts/get_file_if_exists.d.ts.map +1 -0
  76. package/dist/parser_scripts/get_file_if_exists.js +10 -0
  77. package/dist/parser_scripts/get_file_if_exists.js.map +1 -0
  78. package/dist/parser_scripts/get_gradlew_path.d.ts +3 -0
  79. package/dist/parser_scripts/get_gradlew_path.d.ts.map +1 -0
  80. package/dist/parser_scripts/get_gradlew_path.js +24 -0
  81. package/dist/parser_scripts/get_gradlew_path.js.map +1 -0
  82. package/dist/parser_scripts/get_swift_parser_dir.d.ts +2 -0
  83. package/dist/parser_scripts/get_swift_parser_dir.d.ts.map +1 -0
  84. package/dist/parser_scripts/get_swift_parser_dir.js +93 -0
  85. package/dist/parser_scripts/get_swift_parser_dir.js.map +1 -0
  86. package/dist/react/create.d.ts +4 -0
  87. package/dist/react/create.d.ts.map +1 -0
  88. package/dist/react/create.js +132 -0
  89. package/dist/react/create.js.map +1 -0
  90. package/dist/react/parser.d.ts +6 -15
  91. package/dist/react/parser.d.ts.map +1 -1
  92. package/dist/react/parser.js +59 -63
  93. package/dist/react/parser.js.map +1 -1
  94. package/dist/react/parser_template_helpers.d.ts +21 -0
  95. package/dist/react/parser_template_helpers.d.ts.map +1 -1
  96. package/dist/react/parser_template_helpers.js +107 -43
  97. package/dist/react/parser_template_helpers.js.map +1 -1
  98. package/dist/storybook/convert.d.ts +2 -2
  99. package/dist/storybook/convert.d.ts.map +1 -1
  100. package/dist/storybook/convert.js +6 -4
  101. package/dist/storybook/convert.js.map +1 -1
  102. package/package.json +36 -11
  103. package/dist/common/project.d.ts +0 -94
  104. package/dist/common/project.d.ts.map +0 -1
  105. package/dist/common/project.js +0 -198
  106. package/dist/common/project.js.map +0 -1
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSwiftParserDir = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const logging_1 = require("../common/logging");
6
+ const get_file_if_exists_1 = require("./get_file_if_exists");
7
+ // Find the location of the Code Connect Swift package on disk, so that we can
8
+ // call `swift run figma-swift` from the correct location. This requires parsing
9
+ // the output of xcodebuild for .xcodeproj projects, or parsing Package.swift
10
+ // for SPM projects.
11
+ //
12
+ // As this is a first party parser, we do this in TypeScript and call it as part
13
+ // of our code. For third party parsers, logic like this would need to be
14
+ // implemented in a script/binary which the user points Code Connect to.
15
+ async function getSwiftParserDir(cwd, xcodeprojPath) {
16
+ let figmaPackageDir;
17
+ // Check for the supported project types.
18
+ const xcodeProjFile = xcodeprojPath || (0, get_file_if_exists_1.getFileIfExists)(cwd, '*.xcodeproj');
19
+ const packageSwiftFile = (0, get_file_if_exists_1.getFileIfExists)(cwd, 'Package.swift');
20
+ if (!(xcodeProjFile || packageSwiftFile)) {
21
+ (0, logging_1.exitWithError)('No supported project found. Supported project types are .xcodeproj or Package.swift. You can specify the location of your .xcodeproj file with the `xcodeprojPath` config option.');
22
+ }
23
+ if (xcodeProjFile) {
24
+ // Use xcodebuild to get the build settings, so we can find where the Code
25
+ // Connect Swift package is installed
26
+ const buildSettings = (0, child_process_1.execSync)(`xcodebuild -project ${xcodeProjFile} -showBuildSettings`, {
27
+ cwd,
28
+ }).toString();
29
+ // Extract the source and version of the Code Connect Swift package from the
30
+ // xcodebuild output, which can be in any of the following formats depending
31
+ // on how it is installed:
32
+ // - Figma: https://github.com/figma/code-connect @ 0.1.2
33
+ // - Figma: /path/to/code-connect @ local
34
+ // - Figma: /path/to/code-connect
35
+ const figmaPackageMatch = buildSettings.match(/\s+Figma: ([^\s]*)(?: @ (.*))?/);
36
+ if (!figmaPackageMatch) {
37
+ (0, logging_1.exitWithError)('Code Connect Swift package not found');
38
+ }
39
+ // Find the package's location on disk, to compile and run the parser binary from
40
+ const [_, figmaPackageSource, figmaPackageVersion] = figmaPackageMatch;
41
+ // The package version is `local` if installed via the "Add package" dialog,
42
+ // or undefined if installed via the "Frameworks" section directly (which we
43
+ // do for our test project, because the dialog won't allow ancestors to be
44
+ // added)
45
+ const isLocalFigmaPackage = figmaPackageVersion === 'local' || figmaPackageVersion === undefined;
46
+ if (isLocalFigmaPackage) {
47
+ // If the version is 'local', the package is installed from a local checkout,
48
+ // and the path on disk is the source output by xcodebuild.
49
+ figmaPackageDir = figmaPackageSource;
50
+ }
51
+ else {
52
+ // Otherwise, the package will be installed to
53
+ // <DerviedData>/SourcePackages/checkouts/code-connect. We find the
54
+ // DerivedData location from the BUILD_DIR (which points to
55
+ // <DerivedData>/Build/Products).
56
+ const buildDir = buildSettings.match(/\s+BUILD_DIR = (.*)/);
57
+ if (!buildDir) {
58
+ (0, logging_1.exitWithError)('BUILD_DIR not found in xcodebuild output');
59
+ }
60
+ figmaPackageDir = `${buildDir[1]}/../../SourcePackages/checkouts/code-connect`;
61
+ }
62
+ }
63
+ else if (packageSwiftFile) {
64
+ // Use the Swift command to determine if the package is installed locally or from Git
65
+ try {
66
+ const packageInfo = JSON.parse((0, child_process_1.execSync)('swift package describe --type json', { cwd }).toString());
67
+ const codeConnectPackage = packageInfo.dependencies.find((p) => p.identity === 'code-connect') ??
68
+ // Our local directory is called figmadoc, so this is what swift outputs as the identity
69
+ packageInfo.dependencies.find((p) => p.identity === 'figmadoc');
70
+ if (!codeConnectPackage) {
71
+ (0, logging_1.exitWithError)('Code Connect Swift package not found in Package.swift');
72
+ }
73
+ if (codeConnectPackage.path) {
74
+ // Installed locally
75
+ figmaPackageDir = codeConnectPackage.path;
76
+ }
77
+ else {
78
+ // Installed from git, SPM clones it into this directory
79
+ figmaPackageDir = '.build/checkouts/code-connect';
80
+ }
81
+ }
82
+ catch (e) {
83
+ (0, logging_1.exitWithError)(`Error calling Swift command: ${e}`);
84
+ }
85
+ }
86
+ if (!figmaPackageDir) {
87
+ (0, logging_1.exitWithError)('Figma package could not be found');
88
+ }
89
+ logging_1.logger.info(`Found Code Connect Swift package at ${figmaPackageDir}, building parser binary. This may take a few minutes if this is the first time you've run Code Connect.`);
90
+ return figmaPackageDir;
91
+ }
92
+ exports.getSwiftParserDir = getSwiftParserDir;
93
+ //# sourceMappingURL=get_swift_parser_dir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get_swift_parser_dir.js","sourceRoot":"","sources":["../../src/parser_scripts/get_swift_parser_dir.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import z from 'zod';
2
+ import { CreateRequestPayload, CreateResponsePayload } from '../connect/parser_executable_types';
3
+ export declare function createReactCodeConnect(payload: CreateRequestPayload): Promise<z.infer<typeof CreateResponsePayload>>;
4
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/react/create.ts"],"names":[],"mappings":"AAGA,OAAO,CAAC,MAAM,KAAK,CAAA;AACnB,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAA;AAyFhG,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC,CAoChD"}
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.createReactCodeConnect = void 0;
30
+ const lodash_1 = require("lodash");
31
+ const prettier = __importStar(require("prettier"));
32
+ const fs_1 = __importDefault(require("fs"));
33
+ const path_1 = __importDefault(require("path"));
34
+ function isBooleanKind(propValue) {
35
+ const normalized = propValue.toLowerCase();
36
+ return (normalized === 'true' ||
37
+ normalized === 'false' ||
38
+ normalized === 'yes' ||
39
+ normalized === 'no' ||
40
+ normalized === 'on' ||
41
+ normalized === 'off');
42
+ }
43
+ function normalizePropName(name) {
44
+ return name.replace(/#[0-9:]*/g, '');
45
+ }
46
+ function generateCodePropName(name) {
47
+ return (0, lodash_1.camelCase)(name.replace(/[^a-zA-Z]/g, ''));
48
+ }
49
+ function normalizePropValue(name) {
50
+ // Convert the string to kebab-case
51
+ return name.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase();
52
+ }
53
+ function generateProps(component) {
54
+ const props = [];
55
+ if (!component.componentPropertyDefinitions ||
56
+ Object.keys(component.componentPropertyDefinitions).length === 0) {
57
+ return `{}`;
58
+ }
59
+ for (const [propName, propDef] of Object.entries(component.componentPropertyDefinitions)) {
60
+ const codePropName = generateCodePropName(propName);
61
+ const figmaPropName = normalizePropName(propName);
62
+ if (propDef.type === 'BOOLEAN') {
63
+ props.push(`"${codePropName}": figma.boolean('${figmaPropName}')`);
64
+ }
65
+ if (propDef.type === 'TEXT') {
66
+ props.push(`"${codePropName}": figma.string('${figmaPropName}')`);
67
+ }
68
+ if (propDef.type === 'VARIANT') {
69
+ if (propDef.variantOptions?.find((value) => isBooleanKind(value))) {
70
+ props.push(`"${codePropName}": figma.boolean('${figmaPropName}')`);
71
+ }
72
+ else {
73
+ props.push(`"${codePropName}": figma.enum('${figmaPropName}', { \n${propDef.variantOptions
74
+ ?.map((value) => ` "${value}": "${normalizePropValue(value)}"`)
75
+ .join(',\n')}})`);
76
+ }
77
+ }
78
+ if (propDef.type === 'INSTANCE_SWAP') {
79
+ props.push(`"${codePropName}": figma.instance('${figmaPropName}')`);
80
+ }
81
+ }
82
+ return `{
83
+ ${props.join(',\n ')}
84
+ }`;
85
+ }
86
+ function getOutFileName({ outFile, outDir, componentName, }) {
87
+ if (outFile) {
88
+ return outFile;
89
+ }
90
+ const baseName = `${componentName}.figma.tsx`;
91
+ if (outDir) {
92
+ return path_1.default.join(outDir, baseName);
93
+ }
94
+ return path_1.default.join(process.env.INIT_CWD ?? process.cwd(), baseName);
95
+ }
96
+ async function createReactCodeConnect(payload) {
97
+ const { component, destinationFile, destinationDir } = payload;
98
+ const { normalizedName, figmaNodeUrl } = component;
99
+ const filePath = getOutFileName({
100
+ outFile: destinationFile,
101
+ outDir: destinationDir,
102
+ componentName: normalizedName,
103
+ });
104
+ const codeConnect = `
105
+ import React from 'react'
106
+ import { ${normalizedName} } from './${normalizedName}'
107
+ import figma from '@figma/code-connect'
108
+
109
+ /**
110
+ * -- This file was auto-generated by \`figma connect create\` --
111
+ * \`props\` includes a mapping from Figma properties and variants to
112
+ * suggested values. You should update this to match the props of your
113
+ * code component, and update the \`example\` function to return the
114
+ * code example you'd like to see in Figma
115
+ */
116
+
117
+ figma.connect(${normalizedName}, "${figmaNodeUrl}", {
118
+ props: ${generateProps(component)},
119
+ example: (props) => <${normalizedName} />
120
+ })
121
+ `;
122
+ let formatted = await prettier.format(codeConnect, {
123
+ parser: 'typescript',
124
+ semi: false,
125
+ trailingComma: 'all',
126
+ });
127
+ fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
128
+ fs_1.default.writeFileSync(filePath, formatted);
129
+ return { createdFiles: [{ filePath }], messages: [] };
130
+ }
131
+ exports.createReactCodeConnect = createReactCodeConnect;
132
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/react/create.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import ts from 'typescript';
2
- import { CodeConnectConfig } from '../common/project';
3
- import { Intrinsic } from '../common/intrinsics';
2
+ import { CodeConnectReactConfig } from '../connect/project';
3
+ import { PropMappings } from '../common/intrinsics';
4
4
  interface ParserErrorContext {
5
5
  sourceFile: ts.SourceFile;
6
6
  node: ts.Node | undefined;
@@ -18,18 +18,9 @@ export declare class InternalError extends ParserError {
18
18
  export interface ParserContext {
19
19
  checker: ts.TypeChecker;
20
20
  sourceFile: ts.SourceFile;
21
- config: CodeConnectConfig | undefined;
21
+ config: CodeConnectReactConfig;
22
+ absPath: string;
22
23
  }
23
- /**
24
- * Parsers the `props` field of a `figma.connect()` call, returning a mapping of
25
- * prop names to their respective intrinsic types
26
- *
27
- * @param objectLiteral An object literal expression
28
- * @param parserContext Parser context
29
- * @returns
30
- */
31
- export declare function parsePropsObject(objectLiteral: ts.ObjectLiteralExpression, parserContext: ParserContext): PropMappings;
32
- export type PropMappings = Record<string, Intrinsic>;
33
24
  /**
34
25
  * Extract metadata about the referenced React component. Used by both the
35
26
  * Code Connect and Storybook commands.
@@ -40,7 +31,7 @@ export type PropMappings = Record<string, Intrinsic>;
40
31
  * @param node The node being parsed. Used for error logging.
41
32
  * @returns Metadata object
42
33
  */
43
- export declare function parseComponentMetadata(node: ts.PropertyAccessExpression | ts.Identifier | ts.Expression, { checker, sourceFile }: ParserContext): Promise<{
34
+ export declare function parseComponentMetadata(node: ts.PropertyAccessExpression | ts.Identifier | ts.Expression, { checker, sourceFile, absPath }: ParserContext): Promise<{
44
35
  source: string;
45
36
  line: number;
46
37
  component: string;
@@ -72,6 +63,6 @@ export declare function parseRenderFunction(exp: ts.ArrowFunction | ts.FunctionE
72
63
  nestable: boolean;
73
64
  };
74
65
  export declare function getDefaultTemplate(componentMetadata: Awaited<ReturnType<typeof parseComponentMetadata>>): string;
75
- export declare function parse(program: ts.Program, file: string, repoUrl?: string, config?: CodeConnectConfig, debug?: boolean): Promise<any[]>;
66
+ export declare function parse(program: ts.Program, file: string, config: CodeConnectReactConfig, absPath: string, repoUrl?: string, debug?: boolean): Promise<any[]>;
76
67
  export {};
77
68
  //# sourceMappingURL=parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/react/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AAC3B,OAAO,EAAE,iBAAiB,EAAuC,MAAM,mBAAmB,CAAA;AAa1F,OAAO,EAEL,SAAS,EAGV,MAAM,sBAAsB,CAAA;AAK7B,UAAU,kBAAkB;IAC1B,UAAU,EAAE,EAAE,CAAC,UAAU,CAAA;IACzB,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,SAAS,CAAA;CAC1B;AAED,qBAAa,WAAY,SAAQ,KAAK;IACpC,kBAAkB,EAAE,EAAE,CAAC,gBAAgB,GAAG,IAAI,CAAA;IAC9C,cAAc,EAAE,MAAM,CAAA;gBAEV,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB;IAUzD,QAAQ;IAQR,aAAa;CAGd;AAED,qBAAa,aAAc,SAAQ,WAAW;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,EAAE,CAAC,WAAW,CAAA;IACvB,UAAU,EAAE,EAAE,CAAC,UAAU,CAAA;IACzB,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAA;CACtC;AAwID;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,EAAE,CAAC,uBAAuB,EACzC,aAAa,EAAE,aAAa,GAC3B,YAAY,CAOd;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;AAEpD;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,EAAE,CAAC,wBAAwB,GAAG,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,UAAU,EACjE,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,aAAa;;;;GAwGvC;AAeD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,WAmBnE;AAkFD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,EAAE,CAAC,aAAa,GAAG,EAAE,CAAC,kBAAkB,GAAG,EAAE,CAAC,mBAAmB,EACtE,aAAa,EAAE,aAAa,EAC5B,YAAY,CAAC,EAAE,YAAY;;;;;;;EAiP5B;AA2OD,wBAAgB,kBAAkB,CAChC,iBAAiB,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,UAItE;AAiHD,wBAAsB,KAAK,CACzB,OAAO,EAAE,EAAE,CAAC,OAAO,EACnB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,iBAAiB,EAC1B,KAAK,CAAC,EAAE,OAAO,GACd,OAAO,CAAC,GAAG,EAAE,CAAC,CAkChB"}
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/react/parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AAC3B,OAAO,EAAE,sBAAsB,EAAuC,MAAM,oBAAoB,CAAA;AAahG,OAAO,EAGL,YAAY,EAGb,MAAM,sBAAsB,CAAA;AAM7B,UAAU,kBAAkB;IAC1B,UAAU,EAAE,EAAE,CAAC,UAAU,CAAA;IACzB,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,SAAS,CAAA;CAC1B;AAED,qBAAa,WAAY,SAAQ,KAAK;IACpC,kBAAkB,EAAE,EAAE,CAAC,gBAAgB,GAAG,IAAI,CAAA;IAC9C,cAAc,EAAE,MAAM,CAAA;gBAEV,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB;IAUzD,QAAQ;IAQR,aAAa;CAGd;AAED,qBAAa,aAAc,SAAQ,WAAW;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,EAAE,CAAC,WAAW,CAAA;IACvB,UAAU,EAAE,EAAE,CAAC,UAAU,CAAA;IACzB,MAAM,EAAE,sBAAsB,CAAA;IAC9B,OAAO,EAAE,MAAM,CAAA;CAChB;AAwID;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,EAAE,CAAC,wBAAwB,GAAG,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,UAAU,EACjE,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,aAAa;;;;GA8GhD;AAeD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,WAmBnE;AAkFD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,EAAE,CAAC,aAAa,GAAG,EAAE,CAAC,kBAAkB,GAAG,EAAE,CAAC,mBAAmB,EACtE,aAAa,EAAE,aAAa,EAC5B,YAAY,CAAC,EAAE,YAAY;;;;;;;EAmS5B;AA+LD,wBAAgB,kBAAkB,CAChC,iBAAiB,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,UAItE;AAiHD,wBAAsB,KAAK,CACzB,OAAO,EAAE,EAAE,CAAC,OAAO,EACnB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,sBAAsB,EAC9B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,OAAO,GACd,OAAO,CAAC,GAAG,EAAE,CAAC,CAmChB"}
@@ -3,13 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.parse = exports.getDefaultTemplate = exports.parseRenderFunction = exports.isFigmaConnectFile = exports.parseComponentMetadata = exports.parsePropsObject = exports.InternalError = exports.ParserError = void 0;
6
+ exports.parse = exports.getDefaultTemplate = exports.parseRenderFunction = exports.isFigmaConnectFile = exports.parseComponentMetadata = exports.InternalError = exports.ParserError = void 0;
7
7
  const typescript_1 = __importDefault(require("typescript"));
8
- const project_1 = require("../common/project");
8
+ const project_1 = require("../connect/project");
9
9
  const logging_1 = require("../common/logging");
10
10
  const compiler_1 = require("../typescript/compiler");
11
11
  const intrinsics_1 = require("../common/intrinsics");
12
12
  const parser_template_helpers_1 = require("./parser_template_helpers");
13
+ const path_1 = __importDefault(require("path"));
13
14
  class ParserError extends Error {
14
15
  constructor(message, context) {
15
16
  super(message);
@@ -152,23 +153,6 @@ function getImportsForIdentifiers({ sourceFile }, _identifiers) {
152
153
  }
153
154
  return imports;
154
155
  }
155
- /**
156
- * Parsers the `props` field of a `figma.connect()` call, returning a mapping of
157
- * prop names to their respective intrinsic types
158
- *
159
- * @param objectLiteral An object literal expression
160
- * @param parserContext Parser context
161
- * @returns
162
- */
163
- function parsePropsObject(objectLiteral, parserContext) {
164
- const { sourceFile, checker } = parserContext;
165
- return (0, compiler_1.convertObjectLiteralToJs)(objectLiteral, sourceFile, checker, (valueNode) => {
166
- if (typescript_1.default.isCallExpression(valueNode)) {
167
- return (0, intrinsics_1.parseIntrinsic)(valueNode, parserContext);
168
- }
169
- });
170
- }
171
- exports.parsePropsObject = parsePropsObject;
172
156
  /**
173
157
  * Extract metadata about the referenced React component. Used by both the
174
158
  * Code Connect and Storybook commands.
@@ -179,7 +163,7 @@ exports.parsePropsObject = parsePropsObject;
179
163
  * @param node The node being parsed. Used for error logging.
180
164
  * @returns Metadata object
181
165
  */
182
- async function parseComponentMetadata(node, { checker, sourceFile }) {
166
+ async function parseComponentMetadata(node, { checker, sourceFile, absPath }) {
183
167
  let componentSymbol = checker.getSymbolAtLocation(node);
184
168
  let componentSourceFile = sourceFile;
185
169
  let component = '';
@@ -241,7 +225,9 @@ async function parseComponentMetadata(node, { checker, sourceFile }) {
241
225
  else {
242
226
  componentDeclaration = componentSymbol?.declarations?.[0];
243
227
  }
244
- const source = componentSourceFile.fileName;
228
+ // On Windows, fileName is relative whereas on Mac it's absolute, so we need
229
+ // to use path.resolve to build an absolute path
230
+ const source = path_1.default.join(path_1.default.resolve(componentSourceFile.fileName, absPath), path_1.default.basename(componentSourceFile.fileName));
245
231
  if (!source) {
246
232
  throw new InternalError(`Could not find source file for component ${component} - is this file included in the directory passed to \`figma connect <dir>\`?`);
247
233
  }
@@ -381,15 +367,22 @@ function parseRenderFunction(exp, parserContext, propMappings) {
381
367
  // Keep track of any props which are referenced in the example so that we can
382
368
  // insert the appropriate `figma.properties` call in the JS template
383
369
  const referencedProps = new Set();
384
- function createPropPlaceholder(name, node, wrapInJsxExpression = false) {
385
- const mappedProp = propMappings && propMappings[name];
370
+ function createPropPlaceholder({ name, node, wrapInJsxExpression = false, }) {
371
+ let propReferenceName = name;
372
+ // for nested prop references like `nested.prop`, we only want to look for
373
+ // the prop mapping of `nested`, but include the full `nested.prop` in the
374
+ // __PROP__ call
375
+ if (name.includes('.')) {
376
+ propReferenceName = name.split('.')[0];
377
+ }
378
+ const mappedProp = propMappings && propMappings[propReferenceName];
386
379
  if (!mappedProp) {
387
- throw new ParserError(`Could not find prop mapping for ${name} in the props object`, {
380
+ throw new ParserError(`Could not find prop mapping for ${propReferenceName} in the props object`, {
388
381
  sourceFile,
389
382
  node,
390
383
  });
391
384
  }
392
- referencedProps.add(name);
385
+ referencedProps.add(propReferenceName);
393
386
  const callExpression = typescript_1.default.factory.createCallExpression(typescript_1.default.factory.createIdentifier('__PROP__'), undefined, [typescript_1.default.factory.createStringLiteral(name)]);
394
387
  if (wrapInJsxExpression) {
395
388
  return typescript_1.default.factory.createJsxExpression(undefined, callExpression);
@@ -415,8 +408,13 @@ function parseRenderFunction(exp, parserContext, propMappings) {
415
408
  if (typescript_1.default.isIdentifier(propsParameter.name) &&
416
409
  typescript_1.default.isPropertyAccessExpression(node) &&
417
410
  node.expression.getText().startsWith(propsParameter.name.getText())) {
411
+ // nested notation e.g `props.nested.prop`
412
+ if (typescript_1.default.isPropertyAccessExpression(node.expression)) {
413
+ const name = `${node.expression.name.getText()}.${node.name.getText()}`;
414
+ return createPropPlaceholder({ name, node });
415
+ }
418
416
  const name = node.name.getText();
419
- return createPropPlaceholder(name, node);
417
+ return createPropPlaceholder({ name, node });
420
418
  }
421
419
  // `props[""]` notation
422
420
  if (typescript_1.default.isIdentifier(propsParameter.name) &&
@@ -424,15 +422,15 @@ function parseRenderFunction(exp, parserContext, propMappings) {
424
422
  node.expression.getText().startsWith(propsParameter.name.getText()) &&
425
423
  typescript_1.default.isStringLiteral(node.argumentExpression)) {
426
424
  const name = (0, compiler_1.stripQuotes)(node.argumentExpression);
427
- return createPropPlaceholder(name, node);
425
+ return createPropPlaceholder({ name, node });
428
426
  }
429
427
  // object destructuring references
430
428
  if (typescript_1.default.isObjectBindingPattern(propsParameter.name) &&
431
429
  typescript_1.default.isJsxExpression(node) &&
432
430
  node.expression &&
433
- propsParameter.name.elements.find((el) => el.name.getText() === node.expression?.getText())) {
431
+ propsParameter.name.elements.find((el) => node.expression?.getText().startsWith(el.name.getText()))) {
434
432
  const name = node.expression.getText();
435
- return createPropPlaceholder(name, node, true);
433
+ return createPropPlaceholder({ name, node, wrapInJsxExpression: true });
436
434
  }
437
435
  // Replaces {...props} with all the prop mapped props we know about,
438
436
  // e.g. <Button {...props} /> becomes:
@@ -455,7 +453,11 @@ function parseRenderFunction(exp, parserContext, propMappings) {
455
453
  ? Object.keys(propMappings)
456
454
  .filter((prop) => !propsToExclude.includes(prop))
457
455
  .map((prop) => {
458
- return typescript_1.default.factory.createJsxAttribute(typescript_1.default.factory.createIdentifier(prop), createPropPlaceholder(prop, node, true));
456
+ return typescript_1.default.factory.createJsxAttribute(typescript_1.default.factory.createIdentifier(prop), createPropPlaceholder({
457
+ name: prop,
458
+ node,
459
+ wrapInJsxExpression: true,
460
+ }));
459
461
  })
460
462
  : [];
461
463
  if (propMappings) {
@@ -502,15 +504,18 @@ function parseRenderFunction(exp, parserContext, propMappings) {
502
504
  // parser_template_helpers.ts)
503
505
  exampleCode = exampleCode.replace(
504
506
  // match " reactPropName={__PROP__("figmaPropName")}" and extract the names
505
- / ([A-Za-z0-9]+)=\{__PROP__\("([A-Za-z0-9]+)"\)\}/g, (_match, reactPropName, figmaPropName) => {
507
+ / ([A-Za-z0-9]+)=\{__PROP__\("([A-Za-z0-9_\.]+)"\)\}/g, (_match, reactPropName, figmaPropName) => {
506
508
  return `\${_fcc_renderReactProp('${reactPropName}', ${figmaPropName})}`;
507
509
  });
508
510
  // Replace React children placeholders like `${__PROP__("propName")}` with
509
- // `${propName}`. These never need special treatment based on their type.
510
- exampleCode = exampleCode.replace(/\{__PROP__\("([A-Za-z]+)"\)\}/g, '${$1}');
511
+ // calls to _fcc_renderReactChildren, which renders them correctly (see
512
+ // parser_template_helpers.ts)
513
+ exampleCode = exampleCode.replace(/\{__PROP__\("([A-Za-z_\.]+)"\)\}/g, (_match, figmaPropName) => {
514
+ return `\${_fcc_renderReactChildren(${figmaPropName})}`;
515
+ });
511
516
  // Replace object values like `{ prop: __PROP__("propName") }` with
512
517
  // `{ prop: ${propName} }`. These never need special treatment based on their type.
513
- exampleCode = exampleCode.replace(/([A-Za-z0-9]+):\s+__PROP__\("([A-Za-z]+)"\)/g, (_match, objectKey, figmaPropName) => {
518
+ exampleCode = exampleCode.replace(/([A-Za-z0-9]+):\s+__PROP__\("([A-Za-z_\.]+)"\)/g, (_match, objectKey, figmaPropName) => {
514
519
  return `${objectKey}: \${${figmaPropName}}`;
515
520
  });
516
521
  // Generate the template code
@@ -518,6 +523,7 @@ function parseRenderFunction(exp, parserContext, propMappings) {
518
523
  templateCode = (0, parser_template_helpers_1.getParsedTemplateHelpersString)() + '\n\n';
519
524
  // Require the template API
520
525
  templateCode += `const figma = require('figma')\n\n`;
526
+ let nestedLayerCount = 0;
521
527
  // Then we output `const propName = figma.properties.<kind>('propName')` calls
522
528
  // for each referenced prop, so these are accessible to the template code.
523
529
  if (propMappings && referencedProps.size > 0) {
@@ -529,7 +535,20 @@ function parseRenderFunction(exp, parserContext, propMappings) {
529
535
  node: exp,
530
536
  });
531
537
  }
532
- templateCode += `const ${prop} = ${(0, intrinsics_1.intrinsicToString)(propMapping)}\n`;
538
+ if (propMapping.kind === intrinsics_1.IntrinsicKind.NestedProps) {
539
+ // the actual layer name in figma could have a bunch of special characters in it,
540
+ // and if we try to normalize it to a valid JS identifier, it could conflict with
541
+ // other variables in the template code. So we generate a unique variable name
542
+ // for each nested layer reference instead.
543
+ const nestedLayerRef = `nestedLayer${nestedLayerCount++}`;
544
+ templateCode += `const ${nestedLayerRef} = figma.currentLayer.__find__("${propMapping.args.layer}")\n`;
545
+ templateCode += `const ${prop} = {
546
+ ${Object.entries(propMapping.args.props).map(([key, intrinsic]) => `${key}: ${(0, intrinsics_1.intrinsicToString)(intrinsic, nestedLayerRef)}\n`)}
547
+ }\n`;
548
+ }
549
+ else {
550
+ templateCode += `const ${prop} = ${(0, intrinsics_1.intrinsicToString)(propMapping)}\n`;
551
+ }
533
552
  });
534
553
  templateCode += '\n';
535
554
  }
@@ -546,30 +565,6 @@ function parseRenderFunction(exp, parserContext, propMappings) {
546
565
  };
547
566
  }
548
567
  exports.parseRenderFunction = parseRenderFunction;
549
- /**
550
- * This wrapper function ensures that property names are type checked in case
551
- * we make changes to the `figma.connect()` interface.
552
- */
553
- function makeConfigPropertyParser({ key, predicate, errorMessage, }) {
554
- return (configArg, parserContext) => {
555
- if (!configArg) {
556
- return undefined;
557
- }
558
- return (0, compiler_1.parsePropertyOfType)({
559
- objectLiteralNode: configArg,
560
- propertyName: key,
561
- predicate,
562
- parserContext,
563
- required: false,
564
- errorMessage,
565
- });
566
- };
567
- }
568
- function makeFunctionArgumentParser({ index, predicate, required, errorMessage, }) {
569
- return (node, parserContext) => {
570
- return (0, compiler_1.parseFunctionArgument)(node, parserContext, index, predicate, required, errorMessage);
571
- };
572
- }
573
568
  function followIdentifierToStringLiteralDeclaration(node, parserContext, errMessage) {
574
569
  const { checker } = parserContext;
575
570
  let result = node;
@@ -720,7 +715,7 @@ async function parseDoc(node, parserContext, repoUrl) {
720
715
  // but I couldn't work out how to model that in TypeScript
721
716
  const { propsArg, exampleArg, variantArg, linksArg, importsArg } = parseConfigObjectArg(configObjArg, parserContext);
722
717
  let figmaNode = (0, compiler_1.stripQuotes)(figmaNodeUrlArg);
723
- if (config?.documentUrlSubstitutions) {
718
+ if (config.documentUrlSubstitutions) {
724
719
  Object.entries(config.documentUrlSubstitutions).forEach(([from, to]) => {
725
720
  figmaNode = figmaNode.replace(from, to);
726
721
  });
@@ -728,7 +723,7 @@ async function parseDoc(node, parserContext, repoUrl) {
728
723
  const metadata = componentArg
729
724
  ? await parseComponentMetadata(componentArg, parserContext)
730
725
  : undefined;
731
- const props = propsArg ? parsePropsObject(propsArg, parserContext) : undefined;
726
+ const props = propsArg ? (0, intrinsics_1.parsePropsObject)(propsArg, parserContext) : undefined;
732
727
  const render = exampleArg ? parseRenderFunction(exampleArg, parserContext, props) : undefined;
733
728
  const variant = variantArg ? parseVariant(variantArg, sourceFile, checker) : undefined;
734
729
  const links = linksArg ? parseLinks(linksArg, parserContext) : undefined;
@@ -748,7 +743,7 @@ async function parseDoc(node, parserContext, repoUrl) {
748
743
  // If no imports were found, it might mean that the component is not imported, or
749
744
  // that the `figma.connect` call is in the same file as the component. In the latter
750
745
  // case - we'll want to generate one
751
- const fileName = metadata.source.split('/').pop()?.split('.')[0];
746
+ const fileName = metadata.source.split(path_1.default.sep).pop()?.split('.')[0];
752
747
  imports = [
753
748
  {
754
749
  statement: `import { ${metadata.component} } from './${fileName}'`,
@@ -803,7 +798,7 @@ async function parseDoc(node, parserContext, repoUrl) {
803
798
  },
804
799
  };
805
800
  }
806
- async function parse(program, file, repoUrl, config, debug) {
801
+ async function parse(program, file, config, absPath, repoUrl, debug) {
807
802
  const sourceFile = program.getSourceFile(file);
808
803
  if (!sourceFile) {
809
804
  throw new InternalError(`Could not find source file for ${file}`);
@@ -812,6 +807,7 @@ async function parse(program, file, repoUrl, config, debug) {
812
807
  checker: program.getTypeChecker(),
813
808
  sourceFile,
814
809
  config,
810
+ absPath,
815
811
  };
816
812
  const codeConnectObjects = [];
817
813
  const nodes = [parserContext.sourceFile];