@plasmicapp/cli 0.1.162

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 (162) hide show
  1. package/.eslintrc.js +61 -0
  2. package/.idea/cli.iml +11 -0
  3. package/.idea/misc.xml +6 -0
  4. package/.idea/modules.xml +8 -0
  5. package/.idea/vcs.xml +6 -0
  6. package/README +16 -0
  7. package/README.internal +46 -0
  8. package/README.md +17 -0
  9. package/build.sh +8 -0
  10. package/dist/__mocks__/api.d.ts +16 -0
  11. package/dist/__mocks__/api.js +297 -0
  12. package/dist/__tests__/code-utils-spec.d.ts +1 -0
  13. package/dist/__tests__/code-utils-spec.js +838 -0
  14. package/dist/__tests__/ftue-spec.d.ts +1 -0
  15. package/dist/__tests__/ftue-spec.js +39 -0
  16. package/dist/__tests__/project-api-token-spec.d.ts +1 -0
  17. package/dist/__tests__/project-api-token-spec.js +147 -0
  18. package/dist/__tests__/versioned-sync-spec.d.ts +1 -0
  19. package/dist/__tests__/versioned-sync-spec.js +145 -0
  20. package/dist/actions/auth.d.ts +8 -0
  21. package/dist/actions/auth.js +47 -0
  22. package/dist/actions/fix-imports.d.ts +4 -0
  23. package/dist/actions/fix-imports.js +25 -0
  24. package/dist/actions/init.d.ts +62 -0
  25. package/dist/actions/init.js +460 -0
  26. package/dist/actions/project-token.d.ts +6 -0
  27. package/dist/actions/project-token.js +42 -0
  28. package/dist/actions/sync-components.d.ts +10 -0
  29. package/dist/actions/sync-components.js +242 -0
  30. package/dist/actions/sync-global-variants.d.ts +3 -0
  31. package/dist/actions/sync-global-variants.js +89 -0
  32. package/dist/actions/sync-icons.d.ts +7 -0
  33. package/dist/actions/sync-icons.js +92 -0
  34. package/dist/actions/sync-images.d.ts +6 -0
  35. package/dist/actions/sync-images.js +137 -0
  36. package/dist/actions/sync-styles.d.ts +3 -0
  37. package/dist/actions/sync-styles.js +58 -0
  38. package/dist/actions/sync.d.ts +25 -0
  39. package/dist/actions/sync.js +417 -0
  40. package/dist/actions/upload-bundle.d.ts +15 -0
  41. package/dist/actions/upload-bundle.js +28 -0
  42. package/dist/actions/watch.d.ts +14 -0
  43. package/dist/actions/watch.js +90 -0
  44. package/dist/api.d.ts +182 -0
  45. package/dist/api.js +202 -0
  46. package/dist/deps.d.ts +2 -0
  47. package/dist/deps.js +20 -0
  48. package/dist/index.d.ts +7 -0
  49. package/dist/index.js +247 -0
  50. package/dist/lib.d.ts +10 -0
  51. package/dist/lib.js +23 -0
  52. package/dist/migrations/0.1.110-fileLocks.d.ts +2 -0
  53. package/dist/migrations/0.1.110-fileLocks.js +15 -0
  54. package/dist/migrations/0.1.143-ensureImportModuleType.d.ts +2 -0
  55. package/dist/migrations/0.1.143-ensureImportModuleType.js +12 -0
  56. package/dist/migrations/0.1.146-addReactRuntime.d.ts +2 -0
  57. package/dist/migrations/0.1.146-addReactRuntime.js +10 -0
  58. package/dist/migrations/0.1.27-migrateInit.d.ts +1 -0
  59. package/dist/migrations/0.1.27-migrateInit.js +8 -0
  60. package/dist/migrations/0.1.28-tsToTsx.d.ts +3 -0
  61. package/dist/migrations/0.1.28-tsToTsx.js +33 -0
  62. package/dist/migrations/0.1.31-ensureProjectIcons.d.ts +2 -0
  63. package/dist/migrations/0.1.31-ensureProjectIcons.js +12 -0
  64. package/dist/migrations/0.1.42-ensureVersion.d.ts +2 -0
  65. package/dist/migrations/0.1.42-ensureVersion.js +12 -0
  66. package/dist/migrations/0.1.57-ensureJsBundleThemes.d.ts +2 -0
  67. package/dist/migrations/0.1.57-ensureJsBundleThemes.js +12 -0
  68. package/dist/migrations/0.1.64-imageFiles.d.ts +2 -0
  69. package/dist/migrations/0.1.64-imageFiles.js +17 -0
  70. package/dist/migrations/0.1.95-componentType.d.ts +2 -0
  71. package/dist/migrations/0.1.95-componentType.js +16 -0
  72. package/dist/migrations/migrations.d.ts +10 -0
  73. package/dist/migrations/migrations.js +119 -0
  74. package/dist/plasmic.schema.json +463 -0
  75. package/dist/test-common/fixtures.d.ts +13 -0
  76. package/dist/test-common/fixtures.js +165 -0
  77. package/dist/tsconfig-transform.json +68 -0
  78. package/dist/utils/auth-utils.d.ts +31 -0
  79. package/dist/utils/auth-utils.js +236 -0
  80. package/dist/utils/checksum.d.ts +4 -0
  81. package/dist/utils/checksum.js +63 -0
  82. package/dist/utils/code-utils.d.ts +46 -0
  83. package/dist/utils/code-utils.js +457 -0
  84. package/dist/utils/config-utils.d.ts +271 -0
  85. package/dist/utils/config-utils.js +178 -0
  86. package/dist/utils/envdetect.d.ts +4 -0
  87. package/dist/utils/envdetect.js +42 -0
  88. package/dist/utils/error.d.ts +14 -0
  89. package/dist/utils/error.js +42 -0
  90. package/dist/utils/file-utils.d.ts +71 -0
  91. package/dist/utils/file-utils.js +433 -0
  92. package/dist/utils/get-context.d.ts +40 -0
  93. package/dist/utils/get-context.js +339 -0
  94. package/dist/utils/help.d.ts +2 -0
  95. package/dist/utils/help.js +56 -0
  96. package/dist/utils/lang-utils.d.ts +10 -0
  97. package/dist/utils/lang-utils.js +52 -0
  98. package/dist/utils/npm-utils.d.ts +28 -0
  99. package/dist/utils/npm-utils.js +215 -0
  100. package/dist/utils/prompts.d.ts +6 -0
  101. package/dist/utils/prompts.js +23 -0
  102. package/dist/utils/resolve-utils.d.ts +13 -0
  103. package/dist/utils/resolve-utils.js +198 -0
  104. package/dist/utils/semver.d.ts +34 -0
  105. package/dist/utils/semver.js +61 -0
  106. package/dist/utils/test-utils.d.ts +22 -0
  107. package/dist/utils/test-utils.js +106 -0
  108. package/dist/utils/user-utils.d.ts +7 -0
  109. package/dist/utils/user-utils.js +48 -0
  110. package/jest.config.js +6 -0
  111. package/package.json +80 -0
  112. package/src/__mocks__/api.ts +394 -0
  113. package/src/__tests__/code-utils-spec.ts +881 -0
  114. package/src/__tests__/ftue-spec.ts +43 -0
  115. package/src/__tests__/project-api-token-spec.ts +208 -0
  116. package/src/__tests__/versioned-sync-spec.ts +176 -0
  117. package/src/actions/auth.ts +43 -0
  118. package/src/actions/fix-imports.ts +13 -0
  119. package/src/actions/init.ts +638 -0
  120. package/src/actions/project-token.ts +36 -0
  121. package/src/actions/sync-components.ts +405 -0
  122. package/src/actions/sync-global-variants.ts +129 -0
  123. package/src/actions/sync-icons.ts +135 -0
  124. package/src/actions/sync-images.ts +191 -0
  125. package/src/actions/sync-styles.ts +71 -0
  126. package/src/actions/sync.ts +747 -0
  127. package/src/actions/upload-bundle.ts +38 -0
  128. package/src/actions/watch.ts +95 -0
  129. package/src/api.ts +407 -0
  130. package/src/deps.ts +18 -0
  131. package/src/index.ts +300 -0
  132. package/src/lib.ts +10 -0
  133. package/src/migrations/0.1.110-fileLocks.ts +16 -0
  134. package/src/migrations/0.1.146-addReactRuntime.ts +8 -0
  135. package/src/migrations/0.1.27-migrateInit.ts +4 -0
  136. package/src/migrations/0.1.28-tsToTsx.ts +37 -0
  137. package/src/migrations/0.1.31-ensureProjectIcons.ts +10 -0
  138. package/src/migrations/0.1.42-ensureVersion.ts +10 -0
  139. package/src/migrations/0.1.57-ensureJsBundleThemes.ts +10 -0
  140. package/src/migrations/0.1.64-imageFiles.ts +15 -0
  141. package/src/migrations/0.1.95-componentType.ts +14 -0
  142. package/src/migrations/migrations.ts +147 -0
  143. package/src/test-common/fixtures.ts +178 -0
  144. package/src/utils/auth-utils.ts +276 -0
  145. package/src/utils/checksum.ts +106 -0
  146. package/src/utils/code-utils.ts +656 -0
  147. package/src/utils/config-utils.ts +551 -0
  148. package/src/utils/envdetect.ts +39 -0
  149. package/src/utils/error.ts +36 -0
  150. package/src/utils/file-utils.ts +526 -0
  151. package/src/utils/get-context.ts +451 -0
  152. package/src/utils/help.ts +75 -0
  153. package/src/utils/lang-utils.ts +52 -0
  154. package/src/utils/npm-utils.ts +223 -0
  155. package/src/utils/prompts.ts +22 -0
  156. package/src/utils/resolve-utils.ts +245 -0
  157. package/src/utils/semver.ts +67 -0
  158. package/src/utils/test-utils.ts +116 -0
  159. package/src/utils/user-utils.ts +37 -0
  160. package/testData/fixImports_plasmic.json +66 -0
  161. package/tsconfig-transform.json +68 -0
  162. package/tsconfig.json +67 -0
@@ -0,0 +1,405 @@
1
+ import {
2
+ ComponentInfoForMerge,
3
+ makeCachedProjectSyncDataProvider,
4
+ mergeFiles,
5
+ } from "@plasmicapp/code-merger";
6
+ import L from "lodash";
7
+ import path from "upath";
8
+ import { AppServerError, ChecksumBundle, ComponentBundle } from "../api";
9
+ import { logger } from "../deps";
10
+ import { ComponentUpdateSummary, formatAsLocal } from "../utils/code-utils";
11
+ import {
12
+ ComponentConfig,
13
+ CONFIG_FILE_NAME,
14
+ isPageAwarePlatform,
15
+ PlasmicContext,
16
+ ProjectConfig,
17
+ ProjectLock,
18
+ } from "../utils/config-utils";
19
+ import { HandledError } from "../utils/error";
20
+ import {
21
+ defaultPagePath,
22
+ defaultResourcePath,
23
+ deleteFile,
24
+ fileExists,
25
+ readFileContent,
26
+ renameFile,
27
+ writeFileContent,
28
+ } from "../utils/file-utils";
29
+ import { assert, ensure } from "../utils/lang-utils";
30
+ import { confirmWithUser } from "../utils/user-utils";
31
+
32
+ export interface ComponentPendingMerge {
33
+ // path of the skeleton module
34
+ skeletonModulePath: string;
35
+ editedSkeletonFile: string;
36
+ newSkeletonFile: string;
37
+ // function to perform code merger using input whose import has been resolved.
38
+ merge: (
39
+ resolvedNewSkeletonFile: string,
40
+ resolvedEditedSkeletonFile: string
41
+ ) => Promise<void>;
42
+ }
43
+
44
+ const updateDirectSkeleton = async (
45
+ newFileContent: string,
46
+ editedFileContent: string,
47
+ context: PlasmicContext,
48
+ compConfig: ComponentConfig,
49
+ forceOverwrite: boolean,
50
+ nameInIdToUuid: [string, string][],
51
+ appendJsxOnMissingBase: boolean
52
+ ) => {
53
+ // merge code!
54
+ const componentByUuid = new Map<string, ComponentInfoForMerge>();
55
+
56
+ componentByUuid.set(compConfig.id, {
57
+ editedFile: editedFileContent,
58
+ newFile: newFileContent,
59
+ newNameInIdToUuid: new Map(nameInIdToUuid),
60
+ });
61
+ const mergedFiles = await mergeFiles(
62
+ componentByUuid,
63
+ compConfig.projectId,
64
+ makeCachedProjectSyncDataProvider(async (projectId, revision) => {
65
+ try {
66
+ return await context.api.projectSyncMetadata(projectId, revision, true);
67
+ } catch (e) {
68
+ if (
69
+ e instanceof AppServerError &&
70
+ /revision \d+ not found/.test(e.message)
71
+ ) {
72
+ throw e;
73
+ } else {
74
+ throw new HandledError(e.messag);
75
+ }
76
+ }
77
+ }),
78
+ () => {},
79
+ appendJsxOnMissingBase
80
+ );
81
+ const merged = mergedFiles?.get(compConfig.id);
82
+ if (merged) {
83
+ await writeFileContent(context, compConfig.importSpec.modulePath, merged, {
84
+ force: true,
85
+ });
86
+ } else {
87
+ if (!forceOverwrite) {
88
+ throw new HandledError(
89
+ `Cannot merge ${compConfig.importSpec.modulePath}. If you just switched the code scheme for the component from blackbox to direct, use --force-overwrite option to force the switch.`
90
+ );
91
+ } else {
92
+ logger.warn(
93
+ `Overwrite ${compConfig.importSpec.modulePath} despite merge failure`
94
+ );
95
+ await writeFileContent(
96
+ context,
97
+ compConfig.importSpec.modulePath,
98
+ newFileContent,
99
+ {
100
+ force: true,
101
+ }
102
+ );
103
+ }
104
+ }
105
+ };
106
+
107
+ export async function syncProjectComponents(
108
+ context: PlasmicContext,
109
+ project: ProjectConfig,
110
+ version: string,
111
+ componentBundles: ComponentBundle[],
112
+ forceOverwrite: boolean,
113
+ appendJsxOnMissingBase: boolean,
114
+ summary: Map<string, ComponentUpdateSummary>,
115
+ pendingMerge: ComponentPendingMerge[],
116
+ projectLock: ProjectLock,
117
+ checksums: ChecksumBundle,
118
+ baseDir: string
119
+ ) {
120
+ const componentsFromChecksums = new Set([
121
+ ...checksums.cssRulesChecksums.map(([id, _]) => id),
122
+ checksums.renderModuleChecksums.map(([id, _]) => id),
123
+ ]);
124
+ const allCompConfigs = L.keyBy(project.components, (c) => c.id);
125
+ const componentBundleIds = L.keyBy(componentBundles, (i) => i.id);
126
+ const deletedComponents = L.filter(
127
+ allCompConfigs,
128
+ (i) => !componentBundleIds[i.id] && !componentsFromChecksums.has(i.id)
129
+ );
130
+
131
+ const renderModuleFileLocks = L.keyBy(
132
+ projectLock.fileLocks.filter(
133
+ (fileLock) => fileLock.type === "renderModule"
134
+ ),
135
+ (fl) => fl.assetId
136
+ );
137
+ const cssRulesFileLocks = L.keyBy(
138
+ projectLock.fileLocks.filter((fileLock) => fileLock.type === "cssRules"),
139
+ (fl) => fl.assetId
140
+ );
141
+ const id2RenderModuleChecksum = new Map(checksums.renderModuleChecksums);
142
+ const id2CssRulesChecksum = new Map(checksums.cssRulesChecksums);
143
+
144
+ const deletedComponentFiles = new Set<string>();
145
+ for (const deletedComponent of deletedComponents) {
146
+ const componentConfig = allCompConfigs[deletedComponent.id];
147
+ if (
148
+ fileExists(context, componentConfig.renderModuleFilePath) &&
149
+ fileExists(context, componentConfig.cssFilePath)
150
+ ) {
151
+ logger.info(
152
+ `Deleting component: ${componentConfig.name}@${version}\t['${project.projectName}' ${project.projectId}/${componentConfig.id} ${project.version}]`
153
+ );
154
+ deleteFile(context, componentConfig.renderModuleFilePath);
155
+ deleteFile(context, componentConfig.cssFilePath);
156
+ deletedComponentFiles.add(deletedComponent.id);
157
+
158
+ const skeletonPath = componentConfig.importSpec.modulePath;
159
+ if (fileExists(context, skeletonPath)) {
160
+ const deleteSkeleton = await confirmWithUser(
161
+ `Do you want to delete ${skeletonPath}?`,
162
+ context.cliArgs.yes
163
+ );
164
+ if (deleteSkeleton) {
165
+ deleteFile(context, skeletonPath);
166
+ }
167
+ }
168
+ }
169
+ }
170
+ project.components = project.components.filter(
171
+ (c) => !deletedComponentFiles.has(c.id)
172
+ );
173
+
174
+ const deletedComponentIds = new Set(deletedComponents.map((i) => i.id));
175
+ projectLock.fileLocks = projectLock.fileLocks.filter(
176
+ (fileLock) =>
177
+ (fileLock.type !== "renderModule" && fileLock.type !== "cssRules") ||
178
+ !deletedComponentIds.has(fileLock.assetId)
179
+ );
180
+
181
+ for (const bundle of componentBundles) {
182
+ const {
183
+ renderModule,
184
+ skeletonModule,
185
+ cssRules,
186
+ renderModuleFileName,
187
+ skeletonModuleFileName,
188
+ cssFileName,
189
+ componentName,
190
+ id,
191
+ scheme,
192
+ nameInIdToUuid,
193
+ isPage,
194
+ plumeType,
195
+ } = bundle;
196
+ if (context.cliArgs.quiet !== true) {
197
+ logger.info(
198
+ `Syncing component: ${componentName}@${version}\t['${project.projectName}' ${project.projectId}/${id} ${project.version}]`
199
+ );
200
+ }
201
+ let compConfig = allCompConfigs[id];
202
+
203
+ // A component should be regenerated if it is new or path-related information (like the name)
204
+ // changed.
205
+ const shouldRegenerate = compConfig?.name !== componentName;
206
+ let skeletonModuleModified = shouldRegenerate;
207
+
208
+ const skeletonPath = isPage
209
+ ? defaultPagePath(context, skeletonModuleFileName)
210
+ : skeletonModuleFileName;
211
+
212
+ const defaultRenderModuleFilePath = defaultResourcePath(
213
+ context,
214
+ project,
215
+ renderModuleFileName
216
+ );
217
+ const defaultCssFilePath = defaultResourcePath(
218
+ context,
219
+ project,
220
+ cssFileName
221
+ );
222
+
223
+ if (shouldRegenerate) {
224
+ project.components = project.components.filter(
225
+ (existingComponent) => existingComponent.id !== id
226
+ );
227
+ compConfig = {
228
+ id,
229
+ name: componentName,
230
+ type: "managed",
231
+ projectId: project.projectId,
232
+ renderModuleFilePath: defaultRenderModuleFilePath,
233
+ importSpec: { modulePath: skeletonPath },
234
+ cssFilePath: defaultCssFilePath,
235
+ scheme: scheme as "blackbox" | "direct",
236
+ componentType: isPage ? "page" : "component",
237
+ plumeType,
238
+ };
239
+ allCompConfigs[id] = compConfig;
240
+ project.components.push(allCompConfigs[id]);
241
+
242
+ // Because it's the first time, we also generate the skeleton file.
243
+ await writeFileContent(context, skeletonPath, skeletonModule, {
244
+ force: false,
245
+ });
246
+ } else if (compConfig.type === "mapped") {
247
+ } else if (compConfig.type === "managed") {
248
+ // This is an existing component.
249
+ // We only bother touching files on disk if this component is managed.
250
+
251
+ compConfig.componentType = isPage ? "page" : "component";
252
+
253
+ // Read in the existing file
254
+ let editedFile: string;
255
+ try {
256
+ editedFile = readFileContent(context, compConfig.importSpec.modulePath);
257
+ } catch (e) {
258
+ logger.warn(
259
+ `${compConfig.importSpec.modulePath} is missing. If you deleted this component, remember to remove the component from ${CONFIG_FILE_NAME}`
260
+ );
261
+ throw e;
262
+ }
263
+
264
+ const renderModuleFilePath = path.join(
265
+ path.dirname(compConfig.renderModuleFilePath),
266
+ path.basename(defaultRenderModuleFilePath)
267
+ );
268
+ if (
269
+ compConfig.renderModuleFilePath !== renderModuleFilePath &&
270
+ fileExists(context, compConfig.renderModuleFilePath)
271
+ ) {
272
+ if (context.cliArgs.quiet !== true) {
273
+ logger.info(
274
+ `Renaming component file: ${compConfig.renderModuleFilePath}@${version}\t['${project.projectName}' ${project.projectId}/${id} ${project.version}]`
275
+ );
276
+ }
277
+ renameFile(
278
+ context,
279
+ compConfig.renderModuleFilePath,
280
+ renderModuleFilePath
281
+ );
282
+ compConfig.renderModuleFilePath = renderModuleFilePath;
283
+ }
284
+
285
+ const cssFilePath = path.join(
286
+ path.dirname(compConfig.cssFilePath),
287
+ path.basename(defaultCssFilePath)
288
+ );
289
+ if (
290
+ compConfig.cssFilePath !== cssFilePath &&
291
+ fileExists(context, compConfig.cssFilePath)
292
+ ) {
293
+ if (context.cliArgs.quiet !== true) {
294
+ logger.info(
295
+ `Renaming component css file: ${compConfig.cssFilePath}@${version}\t['${project.projectName}' ${project.projectId}/${id} ${project.version}]`
296
+ );
297
+ }
298
+ renameFile(context, compConfig.cssFilePath, cssFilePath);
299
+ compConfig.cssFilePath = cssFilePath;
300
+ }
301
+
302
+ if (
303
+ isPage &&
304
+ isPageAwarePlatform(context.config.platform) &&
305
+ skeletonPath !== compConfig.importSpec.modulePath &&
306
+ fileExists(context, compConfig.importSpec.modulePath)
307
+ ) {
308
+ if (context.cliArgs.quiet !== true) {
309
+ logger.info(
310
+ `Renaming page file: ${compConfig.importSpec.modulePath} -> ${skeletonPath}\t['${project.projectName}' ${project.projectId}/${id} ${project.version}]`
311
+ );
312
+ }
313
+ renameFile(context, compConfig.importSpec.modulePath, skeletonPath);
314
+ compConfig.importSpec.modulePath = skeletonPath;
315
+ }
316
+
317
+ compConfig.plumeType = plumeType;
318
+
319
+ if (scheme === "direct") {
320
+ // We cannot merge right now, but wait until all the imports are resolved
321
+ pendingMerge.push({
322
+ skeletonModulePath: compConfig.importSpec.modulePath,
323
+ editedSkeletonFile: editedFile,
324
+ newSkeletonFile: skeletonModule,
325
+ merge: async (resolvedNewFile, resolvedEditedFile) =>
326
+ updateDirectSkeleton(
327
+ resolvedNewFile,
328
+ resolvedEditedFile,
329
+ context,
330
+ compConfig,
331
+ forceOverwrite,
332
+ nameInIdToUuid,
333
+ appendJsxOnMissingBase
334
+ ),
335
+ });
336
+ skeletonModuleModified = true;
337
+ } else if (/\/\/\s*plasmic-managed-jsx\/\d+/.test(editedFile)) {
338
+ if (forceOverwrite) {
339
+ skeletonModuleModified = true;
340
+ await writeFileContent(
341
+ context,
342
+ compConfig.importSpec.modulePath,
343
+ skeletonModule,
344
+ {
345
+ force: true,
346
+ }
347
+ );
348
+ } else {
349
+ logger.warn(
350
+ `file ${compConfig.importSpec.modulePath} is likely in "direct" scheme. If you intend to switch the code scheme from direct to blackbox, use --force-overwrite option to force the switch.`
351
+ );
352
+ }
353
+ }
354
+ }
355
+
356
+ assert(L.isArray(projectLock.fileLocks));
357
+ // Update FileLocks
358
+ if (renderModuleFileLocks[id]) {
359
+ renderModuleFileLocks[id].checksum = ensure(
360
+ id2RenderModuleChecksum.get(id)
361
+ );
362
+ } else {
363
+ projectLock.fileLocks.push({
364
+ type: "renderModule",
365
+ assetId: id,
366
+ checksum: ensure(id2RenderModuleChecksum.get(id)),
367
+ });
368
+ }
369
+ if (cssRulesFileLocks[id]) {
370
+ cssRulesFileLocks[id].checksum = ensure(id2CssRulesChecksum.get(id));
371
+ } else {
372
+ projectLock.fileLocks.push({
373
+ type: "cssRules",
374
+ assetId: id,
375
+ checksum: ensure(id2CssRulesChecksum.get(id)),
376
+ });
377
+ }
378
+
379
+ if (compConfig.type === "managed") {
380
+ // Again, only need to touch files on disk if managed
381
+ await writeFileContent(
382
+ context,
383
+ compConfig.renderModuleFilePath,
384
+ renderModule,
385
+ {
386
+ force: !shouldRegenerate,
387
+ }
388
+ );
389
+ const formattedCssRules = formatAsLocal(
390
+ cssRules,
391
+ compConfig.cssFilePath,
392
+ baseDir
393
+ );
394
+ await writeFileContent(
395
+ context,
396
+ compConfig.cssFilePath,
397
+ formattedCssRules,
398
+ {
399
+ force: !shouldRegenerate,
400
+ }
401
+ );
402
+ }
403
+ summary.set(id, { skeletonModuleModified });
404
+ }
405
+ }
@@ -0,0 +1,129 @@
1
+ import L from "lodash";
2
+ import path from "upath";
3
+ import { ChecksumBundle, GlobalVariantBundle, ProjectMetaBundle } from "../api";
4
+ import { logger } from "../deps";
5
+ import { formatAsLocal } from "../utils/code-utils";
6
+ import { getOrAddProjectLock, PlasmicContext } from "../utils/config-utils";
7
+ import {
8
+ defaultResourcePath,
9
+ deleteFile,
10
+ fileExists,
11
+ renameFile,
12
+ writeFileContent,
13
+ } from "../utils/file-utils";
14
+ import { ensure } from "../utils/lang-utils";
15
+
16
+ export async function syncGlobalVariants(
17
+ context: PlasmicContext,
18
+ projectMeta: ProjectMetaBundle,
19
+ bundles: GlobalVariantBundle[],
20
+ checksums: ChecksumBundle,
21
+ baseDir: string,
22
+ ) {
23
+ const projectId = projectMeta.projectId;
24
+ const projectLock = getOrAddProjectLock(context, projectId);
25
+ const existingVariantConfigs = L.keyBy(
26
+ context.config.globalVariants.variantGroups.filter(
27
+ (group) => group.projectId === projectId
28
+ ),
29
+ (c) => c.id
30
+ );
31
+ const globalVariantFileLocks = L.keyBy(
32
+ projectLock.fileLocks.filter(
33
+ (fileLock) => fileLock.type === "globalVariant"
34
+ ),
35
+ (fl) => fl.assetId
36
+ );
37
+ const id2VariantChecksum = new Map(checksums.globalVariantChecksums);
38
+
39
+ const variantBundleIds = L.keyBy(bundles, (i) => i.id);
40
+ const deletedGlobalVariants = L.filter(
41
+ existingVariantConfigs,
42
+ (i) => !variantBundleIds[i.id] && !id2VariantChecksum.has(i.id)
43
+ );
44
+
45
+ for (const bundle of bundles) {
46
+ if (context.cliArgs.quiet !== true) {
47
+ logger.info(
48
+ `Syncing global variant ${bundle.name} [${projectId}/${bundle.id}]`
49
+ );
50
+ }
51
+ let variantConfig = existingVariantConfigs[bundle.id];
52
+ const isNew = !variantConfig;
53
+ const defaultContextFilePath = defaultResourcePath(
54
+ context,
55
+ projectMeta,
56
+ bundle.contextFileName
57
+ );
58
+ if (isNew) {
59
+ variantConfig = {
60
+ id: bundle.id,
61
+ name: bundle.name,
62
+ projectId,
63
+ contextFilePath: defaultContextFilePath,
64
+ };
65
+ existingVariantConfigs[bundle.id] = variantConfig;
66
+ context.config.globalVariants.variantGroups.push(variantConfig);
67
+ } else {
68
+ const contextFilePath = path.join(
69
+ path.dirname(variantConfig.contextFilePath),
70
+ path.basename(defaultContextFilePath)
71
+ );
72
+ if (
73
+ variantConfig.contextFilePath !== contextFilePath &&
74
+ fileExists(context, variantConfig.contextFilePath)
75
+ ) {
76
+ if (context.cliArgs.quiet !== true) {
77
+ logger.info(
78
+ `Renaming global variant: ${variantConfig.name} [${projectId}/${bundle.id}]`
79
+ );
80
+ }
81
+ renameFile(context, variantConfig.contextFilePath, contextFilePath);
82
+ variantConfig.contextFilePath = contextFilePath;
83
+ }
84
+ variantConfig.name = bundle.name;
85
+ }
86
+
87
+ // Update FileLocks
88
+ if (globalVariantFileLocks[bundle.id]) {
89
+ globalVariantFileLocks[bundle.id].checksum = ensure(
90
+ id2VariantChecksum.get(bundle.id)
91
+ );
92
+ } else {
93
+ projectLock.fileLocks.push({
94
+ type: "globalVariant",
95
+ assetId: bundle.id,
96
+ checksum: ensure(id2VariantChecksum.get(bundle.id)),
97
+ });
98
+ }
99
+
100
+ await writeFileContent(
101
+ context,
102
+ variantConfig.contextFilePath,
103
+ formatAsLocal(bundle.contextModule, variantConfig.contextFilePath, baseDir),
104
+ { force: !isNew }
105
+ );
106
+ }
107
+
108
+ const deletedVariantsFiles = new Set<string>();
109
+ for (const deletedGlobalVariant of deletedGlobalVariants) {
110
+ const variantConfig = existingVariantConfigs[deletedGlobalVariant.id];
111
+ if (fileExists(context, variantConfig.contextFilePath)) {
112
+ logger.info(
113
+ `Deleting global variant: ${variantConfig.name} [${projectId}/${deletedGlobalVariant.id}]`
114
+ );
115
+ deleteFile(context, variantConfig.contextFilePath);
116
+ deletedVariantsFiles.add(deletedGlobalVariant.id);
117
+ }
118
+ }
119
+ context.config.globalVariants.variantGroups = context.config.globalVariants.variantGroups.filter(
120
+ (v) => !deletedVariantsFiles.has(v.id)
121
+ );
122
+
123
+ const deletedVariantIds = new Set(deletedGlobalVariants.map((i) => i.id));
124
+ projectLock.fileLocks = projectLock.fileLocks.filter(
125
+ (fileLock) =>
126
+ fileLock.type !== "globalVariant" ||
127
+ !deletedVariantIds.has(fileLock.assetId)
128
+ );
129
+ }
@@ -0,0 +1,135 @@
1
+ import L from "lodash";
2
+ import path from "upath";
3
+ import { CommonArgs } from "..";
4
+ import { ChecksumBundle, IconBundle } from "../api";
5
+ import { logger } from "../deps";
6
+ import { formatAsLocal } from "../utils/code-utils";
7
+ import {
8
+ getOrAddProjectConfig,
9
+ getOrAddProjectLock,
10
+ PlasmicContext,
11
+ } from "../utils/config-utils";
12
+ import {
13
+ defaultResourcePath,
14
+ deleteFile,
15
+ fileExists,
16
+ renameFile,
17
+ writeFileContent,
18
+ } from "../utils/file-utils";
19
+ import { ensure } from "../utils/lang-utils";
20
+
21
+ export interface SyncIconsArgs extends CommonArgs {
22
+ projects: readonly string[];
23
+ }
24
+
25
+ export async function syncProjectIconAssets(
26
+ context: PlasmicContext,
27
+ projectId: string,
28
+ version: string,
29
+ iconBundles: IconBundle[],
30
+ checksums: ChecksumBundle,
31
+ baseDir: string,
32
+ ) {
33
+ const project = getOrAddProjectConfig(context, projectId);
34
+ if (!project.icons) {
35
+ project.icons = [];
36
+ }
37
+
38
+ const projectLock = getOrAddProjectLock(context, projectId);
39
+ const knownIconConfigs = L.keyBy(project.icons, (i) => i.id);
40
+ const iconFileLocks = L.keyBy(
41
+ projectLock.fileLocks.filter((fileLock) => fileLock.type === "icon"),
42
+ (fl) => fl.assetId
43
+ );
44
+ const id2IconChecksum = new Map(checksums.iconChecksums);
45
+
46
+ const iconBundleIds = L.keyBy(iconBundles, (i) => i.id);
47
+ const deletedIcons = L.filter(
48
+ knownIconConfigs,
49
+ (i) => !iconBundleIds[i.id] && !id2IconChecksum.has(i.id)
50
+ );
51
+
52
+ for (const bundle of iconBundles) {
53
+ if (context.cliArgs.quiet !== true) {
54
+ logger.info(
55
+ `Syncing icon: ${bundle.name}@${version}\t['${project.projectName}' ${project.projectId}/${bundle.id} ${project.version}]`
56
+ );
57
+ }
58
+ let iconConfig = knownIconConfigs[bundle.id];
59
+ const isNew = !iconConfig;
60
+ const defaultModuleFilePath = defaultResourcePath(
61
+ context,
62
+ project,
63
+ "icons",
64
+ bundle.fileName
65
+ );
66
+ if (isNew) {
67
+ iconConfig = {
68
+ id: bundle.id,
69
+ name: bundle.name,
70
+ moduleFilePath: defaultModuleFilePath,
71
+ };
72
+ knownIconConfigs[bundle.id] = iconConfig;
73
+ project.icons.push(iconConfig);
74
+ } else {
75
+ const moduleFilePath = path.join(
76
+ path.dirname(iconConfig.moduleFilePath),
77
+ path.basename(defaultModuleFilePath)
78
+ );
79
+ if (
80
+ iconConfig.moduleFilePath !== moduleFilePath &&
81
+ fileExists(context, iconConfig.moduleFilePath)
82
+ ) {
83
+ if (context.cliArgs.quiet !== true) {
84
+ logger.info(
85
+ `Renaming icon: ${iconConfig.name}@${version}\t['${project.projectName}' ${project.projectId}/${bundle.id} ${project.version}]`
86
+ );
87
+ }
88
+ renameFile(context, iconConfig.moduleFilePath, moduleFilePath);
89
+ iconConfig.moduleFilePath = moduleFilePath;
90
+ }
91
+ iconConfig.name = bundle.name;
92
+ }
93
+
94
+ // Update FileLocks
95
+ if (iconFileLocks[bundle.id]) {
96
+ iconFileLocks[bundle.id].checksum = ensure(
97
+ id2IconChecksum.get(bundle.id)
98
+ );
99
+ } else {
100
+ projectLock.fileLocks.push({
101
+ type: "icon",
102
+ assetId: bundle.id,
103
+ checksum: ensure(id2IconChecksum.get(bundle.id)),
104
+ });
105
+ }
106
+
107
+ await writeFileContent(
108
+ context,
109
+ iconConfig.moduleFilePath,
110
+ formatAsLocal(bundle.module, iconConfig.moduleFilePath, baseDir),
111
+ {
112
+ force: !isNew,
113
+ }
114
+ );
115
+ }
116
+
117
+ const deletedIconFiles = new Set<string>();
118
+ for (const deletedIcon of deletedIcons) {
119
+ const iconConfig = knownIconConfigs[deletedIcon.id];
120
+ if (fileExists(context, iconConfig.moduleFilePath)) {
121
+ logger.info(
122
+ `Deleting icon: ${iconConfig.name}@${version}\t['${project.projectName}' ${project.projectId}/${deletedIcon.id} ${project.version}]`
123
+ );
124
+ deleteFile(context, iconConfig.moduleFilePath);
125
+ deletedIconFiles.add(deletedIcon.id);
126
+ }
127
+ }
128
+ project.icons = project.icons.filter((i) => !deletedIconFiles.has(i.id));
129
+
130
+ const deletedIconIds = new Set(deletedIcons.map((i) => i.id));
131
+ projectLock.fileLocks = projectLock.fileLocks.filter(
132
+ (fileLock) =>
133
+ fileLock.type !== "icon" || !deletedIconIds.has(fileLock.assetId)
134
+ );
135
+ }