@plasmicapp/cli 0.1.316 → 0.1.318

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.
@@ -404,6 +404,10 @@
404
404
  "description": "Project name synced down from Studio",
405
405
  "type": "string"
406
406
  },
407
+ "splitsProviderFilePath": {
408
+ "description": "File location for the project-wide splits provider. Relative to srcDir",
409
+ "type": "string"
410
+ },
407
411
  "version": {
408
412
  "description": "A version range for syncing this project. Can be:\n* \"latest\" - always syncs down whatever has been saved in the project.\n* \">0\" - always syncs down the latest published version of the project.\n* any other semver string you'd like",
409
413
  "type": "string"
@@ -418,6 +422,7 @@
418
422
  "indirect",
419
423
  "projectId",
420
424
  "projectName",
425
+ "splitsProviderFilePath",
421
426
  "version"
422
427
  ],
423
428
  "type": "object"
@@ -135,6 +135,8 @@ export interface ProjectConfig {
135
135
  cssFilePath: string;
136
136
  /** File location for the project-wide global contexts. Relative to srcDir */
137
137
  globalContextsFilePath: string;
138
+ /** File location for the project-wide splits provider. Relative to srcDir */
139
+ splitsProviderFilePath: string;
138
140
  jsBundleThemes?: JsBundleThemeConfig[];
139
141
  codeComponents?: CodeComponentConfig[];
140
142
  customFunctions?: CustomFunctionConfig[];
@@ -233,7 +235,7 @@ export interface GlobalVariantGroupConfig {
233
235
  contextFilePath: string;
234
236
  }
235
237
  export interface FileLock {
236
- type: "renderModule" | "cssRules" | "icon" | "image" | "projectCss" | "globalVariant" | "globalContexts";
238
+ type: "renderModule" | "cssRules" | "icon" | "image" | "projectCss" | "globalVariant" | "globalContexts" | "splitsProvider";
237
239
  checksum: string;
238
240
  assetId: string;
239
241
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plasmicapp/cli",
3
- "version": "0.1.316",
3
+ "version": "0.1.318",
4
4
  "description": "plasmic cli for syncing local code with Plasmic designs",
5
5
  "engines": {
6
6
  "node": ">=12"
@@ -78,5 +78,5 @@
78
78
  "wrap-ansi": "^7.0.0",
79
79
  "yargs": "^15.4.1"
80
80
  },
81
- "gitHead": "3b71c9fba9c00aea3d7197feefc45da8813b7e7a"
81
+ "gitHead": "f9ffe94b022627f4677d77f8d953e27a4076dc39"
82
82
  }
@@ -355,6 +355,7 @@ class PlasmicApi {
355
355
  globalVariantChecksums: [],
356
356
  projectCssChecksum: "",
357
357
  globalContextsChecksum: "",
358
+ splitsProviderChecksum: "",
358
359
  } as ChecksumBundle,
359
360
  usedNpmPackages: [],
360
361
  externalCssImports: [],
@@ -20,6 +20,7 @@ import { getContext, getCurrentOrDefaultAuth } from "../utils/get-context";
20
20
  import { tuple } from "../utils/lang-utils";
21
21
  import { DEFAULT_GLOBAL_CONTEXTS_NAME } from "./sync-global-contexts";
22
22
  import { ensureImageAssetContents } from "./sync-images";
23
+ import { DEFAULT_SPLITS_PROVIDER_NAME } from "./sync-splits-provider";
23
24
 
24
25
  export interface ExportArgs extends CommonArgs {
25
26
  projects: readonly string[];
@@ -130,6 +131,13 @@ export async function exportProjectsCli(opts: ExportArgs): Promise<void> {
130
131
  );
131
132
  }
132
133
 
134
+ if (bundle.projectConfig.splitsProviderBundle) {
135
+ writeFile(
136
+ `${DEFAULT_SPLITS_PROVIDER_NAME}.${extx}`,
137
+ bundle.projectConfig.splitsProviderBundle.module
138
+ );
139
+ }
140
+
133
141
  await Promise.all(promises);
134
142
  };
135
143
 
@@ -179,6 +187,9 @@ export async function exportProjectsCli(opts: ExportArgs): Promise<void> {
179
187
  globalContextsFilePath: bundle.projectConfig.globalContextBundle
180
188
  ? `${projectName}/${DEFAULT_GLOBAL_CONTEXTS_NAME}.${extx}`
181
189
  : "",
190
+ splitsProviderFilePath: bundle.projectConfig.splitsProviderBundle
191
+ ? `${projectName}/${DEFAULT_SPLITS_PROVIDER_NAME}.${extx}`
192
+ : "",
182
193
  components: bundle.components.map((comp) => ({
183
194
  id: comp.id,
184
195
  name: comp.componentName,
@@ -326,6 +337,14 @@ async function exportProjects(api: PlasmicApi, opts: ExportOpts) {
326
337
  );
327
338
  proj.projectConfig.globalContextBundle.contextModule = res[1];
328
339
  }
340
+ if (proj.projectConfig.splitsProviderBundle) {
341
+ const res = maybeConvertTsxToJsx(
342
+ `${DEFAULT_SPLITS_PROVIDER_NAME}.tsx`,
343
+ proj.projectConfig.splitsProviderBundle.module,
344
+ "."
345
+ );
346
+ proj.projectConfig.splitsProviderBundle.module = res[1];
347
+ }
329
348
  }
330
349
  }
331
350
 
@@ -0,0 +1,88 @@
1
+ import L from "lodash";
2
+ import { ChecksumBundle, ProjectMetaBundle } from "../api";
3
+ import { logger } from "../deps";
4
+ import { formatScript, tsxToJsx } from "../utils/code-utils";
5
+ import {
6
+ PlasmicContext,
7
+ ProjectConfig,
8
+ ProjectLock,
9
+ } from "../utils/config-utils";
10
+ import {
11
+ defaultResourcePath,
12
+ deleteFile,
13
+ fileExists,
14
+ writeFileContent,
15
+ } from "../utils/file-utils";
16
+
17
+ const COMPONENT_NAME = "PlasmicSplitsProvider";
18
+ export const DEFAULT_SPLITS_PROVIDER_NAME = COMPONENT_NAME;
19
+
20
+ export async function syncSplitsProvider(
21
+ context: PlasmicContext,
22
+ projectMeta: ProjectMetaBundle,
23
+ projectConfig: ProjectConfig,
24
+ projectLock: ProjectLock,
25
+ checksums: ChecksumBundle,
26
+ baseDir: string
27
+ ) {
28
+ const resourcePath = getSplitsProviderResourcePath(context, projectConfig);
29
+ if (checksums.splitsProviderChecksum && projectMeta.splitsProviderBundle) {
30
+ if (context.cliArgs.quiet !== true) {
31
+ logger.info(
32
+ `Syncing component: ${COMPONENT_NAME}@${projectLock.version}\t['${projectConfig.projectName}' ${projectConfig.projectId} ${projectConfig.version}]`
33
+ );
34
+ }
35
+ if (context.config.code.lang === "js") {
36
+ projectMeta.splitsProviderBundle.module = formatScript(
37
+ tsxToJsx(projectMeta.splitsProviderBundle.module),
38
+ baseDir
39
+ );
40
+ }
41
+ writeFileContent(
42
+ context,
43
+ resourcePath,
44
+ projectMeta.splitsProviderBundle.module,
45
+ { force: true }
46
+ );
47
+ projectConfig.splitsProviderFilePath = resourcePath;
48
+ const fl = projectLock.fileLocks.find(
49
+ (fl) =>
50
+ fl.assetId === projectConfig.projectId && fl.type === "splitsProvider"
51
+ );
52
+ if (fl) {
53
+ fl.checksum = checksums.splitsProviderChecksum;
54
+ } else {
55
+ projectLock.fileLocks.push({
56
+ assetId: projectConfig.projectId,
57
+ checksum: checksums.splitsProviderChecksum,
58
+ type: "splitsProvider",
59
+ });
60
+ }
61
+ } else if (
62
+ !checksums.splitsProviderChecksum &&
63
+ !projectMeta.splitsProviderBundle
64
+ ) {
65
+ if (fileExists(context, resourcePath)) {
66
+ deleteFile(context, resourcePath);
67
+ }
68
+ projectConfig.splitsProviderFilePath = "";
69
+ L.remove(
70
+ projectLock.fileLocks,
71
+ (fl) =>
72
+ fl.assetId === projectConfig.projectId && fl.type === "splitsProvider"
73
+ );
74
+ }
75
+ }
76
+
77
+ export function getSplitsProviderResourcePath(
78
+ context: PlasmicContext,
79
+ projectConfig: ProjectConfig
80
+ ) {
81
+ return projectConfig.splitsProviderFilePath !== ""
82
+ ? projectConfig.splitsProviderFilePath
83
+ : defaultResourcePath(
84
+ context,
85
+ projectConfig,
86
+ `${COMPONENT_NAME}.${context.config.code.lang === "ts" ? "tsx" : "jsx"}`
87
+ );
88
+ }
@@ -56,6 +56,7 @@ import { syncGlobalContexts } from "./sync-global-contexts";
56
56
  import { syncGlobalVariants } from "./sync-global-variants";
57
57
  import { syncProjectIconAssets } from "./sync-icons";
58
58
  import { syncProjectImageAssets } from "./sync-images";
59
+ import { syncSplitsProvider } from "./sync-splits-provider";
59
60
  import { upsertStyleTokens } from "./sync-styles";
60
61
 
61
62
  export interface SyncArgs extends CommonArgs {
@@ -774,6 +775,15 @@ async function syncProjectConfig(
774
775
  baseDir
775
776
  );
776
777
 
778
+ await syncSplitsProvider(
779
+ context,
780
+ projectBundle,
781
+ projectConfig,
782
+ projectLock,
783
+ checksums,
784
+ baseDir
785
+ );
786
+
777
787
  // Write out components
778
788
  await syncProjectComponents(
779
789
  context,
package/src/api.ts CHANGED
@@ -46,6 +46,11 @@ export interface GlobalContextBundle {
46
46
  contextModule: string;
47
47
  }
48
48
 
49
+ export interface SplitsProviderBundle {
50
+ id: string;
51
+ module: string;
52
+ }
53
+
49
54
  export interface JsBundleTheme {
50
55
  themeFileName: string;
51
56
  themeModule: string;
@@ -59,6 +64,7 @@ export interface ProjectMetaBundle {
59
64
  cssRules: string;
60
65
  jsBundleThemes?: JsBundleTheme[];
61
66
  globalContextBundle?: GlobalContextBundle;
67
+ splitsProviderBundle?: SplitsProviderBundle;
62
68
  }
63
69
 
64
70
  export interface IconBundle {
@@ -163,6 +169,8 @@ export interface ChecksumBundle {
163
169
  projectCssChecksum: string;
164
170
  // Checksum of project global contexts
165
171
  globalContextsChecksum: string;
172
+ // Checksum of project splits provider
173
+ splitsProviderChecksum: string;
166
174
  }
167
175
 
168
176
  export interface CodeComponentMeta {
@@ -151,6 +151,7 @@ export const project1Config: ProjectConfig = {
151
151
  jsBundleThemes: [],
152
152
  indirect: false,
153
153
  globalContextsFilePath: "",
154
+ splitsProviderFilePath: "",
154
155
  };
155
156
 
156
157
  export function expectProject1PlasmicJson(optional?: {
@@ -30,6 +30,9 @@ function getFilesByFileLockAssetId(
30
30
  globalContext: {
31
31
  [projectConfig.projectId]: projectConfig.globalContextsFilePath,
32
32
  },
33
+ splitsProvider: {
34
+ [projectConfig.projectId]: projectConfig.splitsProviderFilePath,
35
+ },
33
36
  } as const;
34
37
  }
35
38
 
@@ -56,6 +59,7 @@ export function getChecksums(
56
59
  globalVariantChecksums: [],
57
60
  projectCssChecksum: "",
58
61
  globalContextsChecksum: "",
62
+ splitsProviderChecksum: "",
59
63
  };
60
64
  }
61
65
 
@@ -157,6 +161,20 @@ export function getChecksums(
157
161
  ? globalContextsChecksums[0].checksum
158
162
  : "";
159
163
 
164
+ const splitsProviderChecksums = fileLocks
165
+ .filter(
166
+ (fileLock) =>
167
+ fileLock.type === "splitsProvider" && fileLock.assetId === projectId
168
+ )
169
+ .filter((fileLock) =>
170
+ checkFile(fileLocations.splitsProvider[fileLock.assetId])
171
+ );
172
+ assert(splitsProviderChecksums.length < 2);
173
+ const splitsProviderChecksum =
174
+ splitsProviderChecksums.length > 0
175
+ ? splitsProviderChecksums[0].checksum
176
+ : "";
177
+
160
178
  return {
161
179
  imageChecksums,
162
180
  iconChecksums,
@@ -165,5 +183,6 @@ export function getChecksums(
165
183
  globalVariantChecksums,
166
184
  projectCssChecksum,
167
185
  globalContextsChecksum,
186
+ splitsProviderChecksum,
168
187
  };
169
188
  }
@@ -13,6 +13,7 @@ import {
13
13
  fixComponentCssReferences,
14
14
  fixComponentImagesReferences,
15
15
  } from "../actions/sync-images";
16
+ import { getSplitsProviderResourcePath } from "../actions/sync-splits-provider";
16
17
  import { logger } from "../deps";
17
18
  import { GLOBAL_SETTINGS } from "../globals";
18
19
  import { HandledError } from "../utils/error";
@@ -171,6 +172,7 @@ type PlasmicImportType =
171
172
  | "codeComponentHelper"
172
173
  | "globalContext"
173
174
  | "customFunction"
175
+ | "splitsProvider"
174
176
  | undefined;
175
177
 
176
178
  const validJsIdentifierChars = [
@@ -199,7 +201,7 @@ function tryParsePlasmicImportSpec(node: ImportDeclaration) {
199
201
  "plasmic-import:\\s+([",
200
202
  ...validJsIdentifierChars,
201
203
  "\\.",
202
- "]+)(?:\\/(component|css|render|globalVariant|projectcss|defaultcss|icon|picture|jsBundle|codeComponent|globalContext|customFunction))?",
204
+ "]+)(?:\\/(component|css|render|globalVariant|projectcss|defaultcss|icon|picture|jsBundle|codeComponent|globalContext|customFunction|splitsProvider))?",
203
205
  ].join("")
204
206
  )
205
207
  );
@@ -425,6 +427,18 @@ export function replaceImports(
425
427
  // npm package
426
428
  stmt.source.value = meta.importPath;
427
429
  }
430
+ } else if (type === "splitsProvider") {
431
+ const projectConfig = fixImportContext.projects[uuid];
432
+ if (!projectConfig) {
433
+ throwMissingReference(context, "project", uuid, fromPath);
434
+ }
435
+ const realPath = makeImportPath(
436
+ context,
437
+ fromPath,
438
+ projectConfig.splitsProviderFilePath,
439
+ true
440
+ );
441
+ stmt.source.value = realPath;
428
442
  }
429
443
  });
430
444
 
@@ -590,6 +604,15 @@ export async function fixAllImportStatements(
590
604
  lastError = err;
591
605
  }
592
606
 
607
+ try {
608
+ fixSplitsProviderImportStatements(context, fixImportContext, baseDir);
609
+ } catch (err) {
610
+ logger.error(
611
+ `Error encountered while fixing imports for splits provider: ${err}`
612
+ );
613
+ lastError = err;
614
+ }
615
+
593
616
  if (lastError) {
594
617
  throw lastError;
595
618
  }
@@ -814,3 +837,42 @@ async function fixGlobalContextImportStatements(
814
837
  }
815
838
  }
816
839
  }
840
+
841
+ async function fixSplitsProviderImportStatements(
842
+ context: PlasmicContext,
843
+ fixImportContext: FixImportContext,
844
+ baseDir: string
845
+ ) {
846
+ for (const project of context.config.projects) {
847
+ if (!project.splitsProviderFilePath) continue;
848
+ const resourcePath = getSplitsProviderResourcePath(context, project);
849
+
850
+ let prevContent: string;
851
+ try {
852
+ prevContent = readFileText(
853
+ makeFilePath(context, resourcePath)
854
+ ).toString();
855
+ } catch (e) {
856
+ logger.warn(
857
+ `${resourcePath} is missing. If you deleted this component, remember to remove the component from ${CONFIG_FILE_NAME}`
858
+ );
859
+ throw e;
860
+ }
861
+
862
+ const newContent = replaceImports(
863
+ context,
864
+ prevContent,
865
+ resourcePath,
866
+ fixImportContext,
867
+ false,
868
+ baseDir,
869
+ true
870
+ );
871
+
872
+ if (prevContent !== newContent) {
873
+ await writeFileContent(context, resourcePath, newContent, {
874
+ force: true,
875
+ });
876
+ }
877
+ }
878
+ }
@@ -180,6 +180,8 @@ export interface ProjectConfig {
180
180
  cssFilePath: string;
181
181
  /** File location for the project-wide global contexts. Relative to srcDir */
182
182
  globalContextsFilePath: string;
183
+ /** File location for the project-wide splits provider. Relative to srcDir */
184
+ splitsProviderFilePath: string;
183
185
 
184
186
  // Code-component-related fields can be treated as optional not to be shown
185
187
  // to the users nor appear to be missing in the documentation.
@@ -220,6 +222,7 @@ export function createProjectConfig(base: {
220
222
  images: [],
221
223
  indirect: base.indirect,
222
224
  globalContextsFilePath: "",
225
+ splitsProviderFilePath: "",
223
226
  };
224
227
  }
225
228
 
@@ -326,7 +329,8 @@ export interface FileLock {
326
329
  | "image"
327
330
  | "projectCss"
328
331
  | "globalVariant"
329
- | "globalContexts";
332
+ | "globalContexts"
333
+ | "splitsProvider";
330
334
  // The checksum value for the file
331
335
  checksum: string;
332
336
  // The component id, or the image asset id
@@ -576,6 +580,7 @@ export function getOrAddProjectConfig(
576
580
  jsBundleThemes: [],
577
581
  indirect: false,
578
582
  globalContextsFilePath: "",
583
+ splitsProviderFilePath: "",
579
584
  };
580
585
  context.config.projects.push(project);
581
586
  }
@@ -99,6 +99,8 @@ function removeMissingFilesFromLock(
99
99
  return knownIcons[lock.assetId];
100
100
  case "globalContexts":
101
101
  return knownProjects[project.projectId].globalContextsFilePath;
102
+ case "splitsProvider":
103
+ return knownProjects[project.projectId].splitsProviderFilePath;
102
104
  }
103
105
  });
104
106
 
@@ -230,6 +232,17 @@ async function resolveMissingFilesInConfig(
230
232
  baseNameToFiles
231
233
  )) || project.globalContextsFilePath;
232
234
 
235
+ if (!project.splitsProviderFilePath) {
236
+ project.splitsProviderFilePath = "";
237
+ }
238
+ // Try to find the file, otherwise recreate at either the specified path or default path (if both are falsey)
239
+ project.splitsProviderFilePath =
240
+ (await attemptToRestoreFilePath(
241
+ context,
242
+ project.splitsProviderFilePath,
243
+ baseNameToFiles
244
+ )) || project.splitsProviderFilePath;
245
+
233
246
  project.images = await filterFiles(project.images, "filePath");
234
247
  project.icons = await filterFiles(project.icons, "moduleFilePath");
235
248
  project.jsBundleThemes = await filterFiles(