@plasmicapp/cli 0.1.166 → 0.1.170
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.
- package/dist/__mocks__/api.js +8 -0
- package/dist/__tests__/project-api-token-spec.js +4 -4
- package/dist/actions/sync-global-contexts.d.ts +4 -0
- package/dist/actions/sync-global-contexts.js +61 -0
- package/dist/actions/sync.js +35 -3
- package/dist/api.d.ts +10 -0
- package/dist/api.js +9 -0
- package/dist/index.js +0 -0
- package/dist/plasmic.schema.json +5 -0
- package/dist/test-common/fixtures.d.ts +3 -1
- package/dist/test-common/fixtures.js +5 -2
- package/dist/utils/checksum.js +7 -0
- package/dist/utils/code-utils.js +26 -0
- package/dist/utils/config-utils.d.ts +4 -1
- package/dist/utils/config-utils.js +2 -0
- package/dist/utils/file-utils.js +1 -0
- package/dist/utils/get-context.js +8 -4
- package/dist/utils/resolve-utils.js +32 -4
- package/package.json +1 -1
- package/src/__mocks__/api.ts +7 -0
- package/src/__tests__/project-api-token-spec.ts +8 -4
- package/src/actions/sync-global-contexts.ts +87 -0
- package/src/actions/sync.ts +72 -1
- package/src/api.ts +21 -0
- package/src/test-common/fixtures.ts +7 -2
- package/src/utils/checksum.ts +12 -0
- package/src/utils/code-utils.ts +42 -0
- package/src/utils/config-utils.ts +8 -1
- package/src/utils/file-utils.ts +5 -1
- package/src/utils/get-context.ts +13 -4
- package/src/utils/resolve-utils.ts +46 -10
package/dist/__mocks__/api.js
CHANGED
|
@@ -249,7 +249,10 @@ class PlasmicApi {
|
|
|
249
249
|
iconChecksums: [],
|
|
250
250
|
globalVariantChecksums: [],
|
|
251
251
|
projectCssChecksum: "",
|
|
252
|
+
globalContextsChecksum: "",
|
|
252
253
|
},
|
|
254
|
+
usedNpmPackages: [],
|
|
255
|
+
externalCssImports: [],
|
|
253
256
|
};
|
|
254
257
|
return result;
|
|
255
258
|
});
|
|
@@ -274,6 +277,11 @@ class PlasmicApi {
|
|
|
274
277
|
throw new Error("Unimplemented");
|
|
275
278
|
});
|
|
276
279
|
}
|
|
280
|
+
latestCodegenVersion() {
|
|
281
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
282
|
+
return "0.0.1";
|
|
283
|
+
});
|
|
284
|
+
}
|
|
277
285
|
requiredPackages() {
|
|
278
286
|
return __awaiter(this, void 0, void 0, function* () {
|
|
279
287
|
return {
|
|
@@ -51,20 +51,20 @@ describe("Project API tokens", () => {
|
|
|
51
51
|
fixtures_1.tmpRepo.writePlasmicJson(Object.assign(Object.assign({}, fixtures_1.defaultPlasmicJson), { projects: [Object.assign(Object.assign({}, fixtures_1.project1Config), { projectApiToken: "blah" })] }));
|
|
52
52
|
yield expect(sync_1.sync(fixtures_1.opts)).resolves.toBeUndefined();
|
|
53
53
|
fixtures_1.expectProject1Components();
|
|
54
|
-
fixtures_1.expectProject1PlasmicJson();
|
|
54
|
+
fixtures_1.expectProject1PlasmicJson({ projectApiToken: true });
|
|
55
55
|
// Re-run, this time with no auth.
|
|
56
56
|
removeAuth();
|
|
57
|
-
yield expect(sync_1.sync(fixtures_1.opts)).
|
|
57
|
+
yield expect(sync_1.sync(fixtures_1.opts)).rejects.toThrow("No user+token, and project API tokens don't match");
|
|
58
58
|
}));
|
|
59
59
|
test("is filled in by auth'd user if project exists but token was initially missing", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
60
60
|
fixtures_1.opts.projects = ["projectId1"];
|
|
61
61
|
fixtures_1.tmpRepo.writePlasmicJson(Object.assign(Object.assign({}, fixtures_1.defaultPlasmicJson), { projects: [fixtures_1.project1Config] }));
|
|
62
62
|
yield expect(sync_1.sync(fixtures_1.opts)).resolves.toBeUndefined();
|
|
63
63
|
fixtures_1.expectProject1Components();
|
|
64
|
-
fixtures_1.expectProject1PlasmicJson();
|
|
64
|
+
fixtures_1.expectProject1PlasmicJson({ projectApiToken: true });
|
|
65
65
|
// Re-run, this time with no auth.
|
|
66
66
|
removeAuth();
|
|
67
|
-
yield expect(sync_1.sync(fixtures_1.opts)).
|
|
67
|
+
yield expect(sync_1.sync(fixtures_1.opts)).rejects.toThrow("Unable to authenticate Plasmic. Please run 'plasmic auth' or check the projectApiTokens in your plasmic.json, and try again.");
|
|
68
68
|
}));
|
|
69
69
|
test("when not available, should prompt for auth", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
70
70
|
fixtures_1.opts.projects = ["projectId1"];
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ChecksumBundle, ProjectMetaBundle } from "../api";
|
|
2
|
+
import { PlasmicContext, ProjectConfig, ProjectLock } from "../utils/config-utils";
|
|
3
|
+
export declare function syncGlobalContexts(context: PlasmicContext, projectMeta: ProjectMetaBundle, projectConfig: ProjectConfig, projectLock: ProjectLock, checksums: ChecksumBundle, baseDir: string): Promise<void>;
|
|
4
|
+
export declare function getGlobalContextsResourcePath(context: PlasmicContext, projectConfig: ProjectConfig): string;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.getGlobalContextsResourcePath = exports.syncGlobalContexts = void 0;
|
|
16
|
+
const deps_1 = require("../deps");
|
|
17
|
+
const code_utils_1 = require("../utils/code-utils");
|
|
18
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
19
|
+
const file_utils_1 = require("../utils/file-utils");
|
|
20
|
+
const COMPONENT_NAME = "PlasmicGlobalContextsProvider";
|
|
21
|
+
function syncGlobalContexts(context, projectMeta, projectConfig, projectLock, checksums, baseDir) {
|
|
22
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
const resourcePath = getGlobalContextsResourcePath(context, projectConfig);
|
|
24
|
+
if (checksums.globalContextsChecksum && projectMeta.globalContextBundle) {
|
|
25
|
+
if (context.cliArgs.quiet !== true) {
|
|
26
|
+
deps_1.logger.info(`Syncing component: ${COMPONENT_NAME}@${projectLock.version}\t['${projectConfig.projectName}' ${projectConfig.projectId} ${projectConfig.version}]`);
|
|
27
|
+
}
|
|
28
|
+
if (context.config.code.lang === "js") {
|
|
29
|
+
projectMeta.globalContextBundle.contextModule = code_utils_1.formatScript(code_utils_1.tsxToJsx(projectMeta.globalContextBundle.contextModule), baseDir);
|
|
30
|
+
}
|
|
31
|
+
file_utils_1.writeFileContent(context, resourcePath, projectMeta.globalContextBundle.contextModule, { force: false });
|
|
32
|
+
projectConfig.globalContextsFilePath = resourcePath;
|
|
33
|
+
const fl = projectLock.fileLocks.find((fl) => fl.assetId === projectConfig.projectId && fl.type === "globalContexts");
|
|
34
|
+
if (fl) {
|
|
35
|
+
fl.checksum = checksums.globalContextsChecksum;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
projectLock.fileLocks.push({
|
|
39
|
+
assetId: projectConfig.projectId,
|
|
40
|
+
checksum: checksums.globalContextsChecksum,
|
|
41
|
+
type: "globalContexts",
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else if (!checksums.globalContextsChecksum &&
|
|
46
|
+
!projectMeta.globalContextBundle) {
|
|
47
|
+
if (file_utils_1.fileExists(context, resourcePath)) {
|
|
48
|
+
file_utils_1.deleteFile(context, resourcePath);
|
|
49
|
+
}
|
|
50
|
+
projectConfig.globalContextsFilePath = "";
|
|
51
|
+
lodash_1.default.remove(projectLock.fileLocks, (fl) => fl.assetId === projectConfig.projectId && fl.type === "globalContexts");
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
exports.syncGlobalContexts = syncGlobalContexts;
|
|
56
|
+
function getGlobalContextsResourcePath(context, projectConfig) {
|
|
57
|
+
return projectConfig.globalContextsFilePath !== ""
|
|
58
|
+
? projectConfig.globalContextsFilePath
|
|
59
|
+
: file_utils_1.defaultResourcePath(context, projectConfig, `${COMPONENT_NAME}.${context.config.code.lang === "ts" ? "tsx" : "jsx"}`);
|
|
60
|
+
}
|
|
61
|
+
exports.getGlobalContextsResourcePath = getGlobalContextsResourcePath;
|
package/dist/actions/sync.js
CHANGED
|
@@ -54,6 +54,7 @@ const sync_global_variants_1 = require("./sync-global-variants");
|
|
|
54
54
|
const sync_icons_1 = require("./sync-icons");
|
|
55
55
|
const sync_images_1 = require("./sync-images");
|
|
56
56
|
const sync_styles_1 = require("./sync-styles");
|
|
57
|
+
const sync_global_contexts_1 = require("./sync-global-contexts");
|
|
57
58
|
function ensureRequiredPackages(context, baseDir, yes) {
|
|
58
59
|
return __awaiter(this, void 0, void 0, function* () {
|
|
59
60
|
const requireds = yield context.api.requiredPackages();
|
|
@@ -208,13 +209,15 @@ function sync(opts, metadataDefaults) {
|
|
|
208
209
|
...versionResolution.dependencies,
|
|
209
210
|
].map((p) => lodash_1.default.pick(p, "projectId", "projectApiToken"));
|
|
210
211
|
context.api.attachProjectIdsAndTokens(projectIdsAndTokens);
|
|
212
|
+
const externalNpmPackages = new Set();
|
|
213
|
+
const externalCssImports = new Set();
|
|
211
214
|
// Perform the actual sync
|
|
212
215
|
yield file_utils_1.withBufferedFs(() => __awaiter(this, void 0, void 0, function* () {
|
|
213
216
|
var _b;
|
|
214
217
|
// Sync in sequence (no parallelism)
|
|
215
218
|
// going in reverse to get leaves of the dependency tree first
|
|
216
219
|
for (const projectMeta of projectsToSync) {
|
|
217
|
-
yield syncProject(context, opts, projectIdsAndTokens, projectMeta.projectId, projectMeta.componentIds, projectMeta.version, projectMeta.dependencies, summary, pendingMerge, projectMeta.indirect, metadataDefaults);
|
|
220
|
+
yield syncProject(context, opts, projectIdsAndTokens, projectMeta.projectId, projectMeta.componentIds, projectMeta.version, projectMeta.dependencies, summary, pendingMerge, projectMeta.indirect, externalNpmPackages, externalCssImports, metadataDefaults);
|
|
218
221
|
}
|
|
219
222
|
// Materialize scheme into each component config.
|
|
220
223
|
context.config.projects.forEach((p) => p.components.forEach((c) => {
|
|
@@ -255,9 +258,22 @@ function sync(opts, metadataDefaults) {
|
|
|
255
258
|
const config = Object.assign(Object.assign({}, loaderConfig), { projects: lodash_1.default.sortBy(lodash_1.default.uniqBy([...freshIdsAndTokens, ...((_b = loaderConfig === null || loaderConfig === void 0 ? void 0 : loaderConfig.projects) !== null && _b !== void 0 ? _b : [])], (p) => p.projectId), (p) => p.projectId) });
|
|
256
259
|
writeLoaderConfig(opts, config);
|
|
257
260
|
}
|
|
261
|
+
const codegenVersion = yield context.api.latestCodegenVersion();
|
|
262
|
+
context.lock.projects.forEach((p) => {
|
|
263
|
+
if (projectsToSync.some((syncedProject) => syncedProject.projectId === p.projectId)) {
|
|
264
|
+
p.codegenVersion = codegenVersion;
|
|
265
|
+
}
|
|
266
|
+
});
|
|
258
267
|
// Write the new ComponentConfigs to disk
|
|
259
268
|
yield config_utils_1.updateConfig(context, context.config, baseDir);
|
|
260
269
|
}));
|
|
270
|
+
yield checkExternalPkgs(context, baseDir, opts, Array.from(externalNpmPackages.keys()));
|
|
271
|
+
if (!opts.quiet && externalCssImports.size > 0) {
|
|
272
|
+
deps_1.logger.info(`This project uses external packages and styles. Make sure to import the following global CSS: ` +
|
|
273
|
+
Array.from(externalCssImports.keys())
|
|
274
|
+
.map((stmt) => `"${stmt}"`)
|
|
275
|
+
.join(", "));
|
|
276
|
+
}
|
|
261
277
|
// Post-sync commands
|
|
262
278
|
if (!opts.ignorePostSync) {
|
|
263
279
|
for (const cmd of context.config.postSyncCommands || []) {
|
|
@@ -272,6 +288,20 @@ function sync(opts, metadataDefaults) {
|
|
|
272
288
|
});
|
|
273
289
|
}
|
|
274
290
|
exports.sync = sync;
|
|
291
|
+
function checkExternalPkgs(context, baseDir, opts, pkgs) {
|
|
292
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
293
|
+
const missingPkgs = pkgs.filter((pkg) => {
|
|
294
|
+
const installedPkg = npm_utils_1.findInstalledVersion(context, baseDir, pkg);
|
|
295
|
+
return !installedPkg;
|
|
296
|
+
});
|
|
297
|
+
if (missingPkgs.length > 0) {
|
|
298
|
+
const upgrade = yield user_utils_1.confirmWithUser(`The following packages aren't installed but are required by some projects, would you like to install them? ${missingPkgs.join(", ")}`, opts.yes);
|
|
299
|
+
if (upgrade) {
|
|
300
|
+
npm_utils_1.installUpgrade(missingPkgs.join(" "), baseDir);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}
|
|
275
305
|
function maybeRenamePathExt(context, path, ext) {
|
|
276
306
|
if (!path) {
|
|
277
307
|
return path;
|
|
@@ -292,7 +322,7 @@ function fixFileExtension(context) {
|
|
|
292
322
|
});
|
|
293
323
|
});
|
|
294
324
|
}
|
|
295
|
-
function syncProject(context, opts, projectIdsAndTokens, projectId, componentIds, projectVersion, dependencies, summary, pendingMerge, indirect, metadataDefaults) {
|
|
325
|
+
function syncProject(context, opts, projectIdsAndTokens, projectId, componentIds, projectVersion, dependencies, summary, pendingMerge, indirect, externalNpmPackages, externalCssImports, metadataDefaults) {
|
|
296
326
|
var _a;
|
|
297
327
|
return __awaiter(this, void 0, void 0, function* () {
|
|
298
328
|
const newComponentScheme = opts.newComponentScheme || context.config.code.scheme;
|
|
@@ -336,6 +366,8 @@ function syncProject(context, opts, projectIdsAndTokens, projectId, componentIds
|
|
|
336
366
|
yield sync_styles_1.upsertStyleTokens(context, projectBundle.usedTokens);
|
|
337
367
|
yield sync_icons_1.syncProjectIconAssets(context, projectId, projectVersion, projectBundle.iconAssets, projectBundle.checksums, opts.baseDir);
|
|
338
368
|
yield sync_images_1.syncProjectImageAssets(context, projectId, projectVersion, projectBundle.imageAssets, projectBundle.checksums);
|
|
369
|
+
(projectBundle.usedNpmPackages || []).forEach((pkg) => externalNpmPackages.add(pkg));
|
|
370
|
+
(projectBundle.externalCssImports || []).forEach((css) => externalCssImports.add(css));
|
|
339
371
|
});
|
|
340
372
|
}
|
|
341
373
|
function syncStyleConfig(context, response) {
|
|
@@ -365,7 +397,6 @@ function syncProjectConfig(context, projectBundle, projectApiToken, version, dep
|
|
|
365
397
|
if (!projectConfig.cssFilePath) {
|
|
366
398
|
projectConfig.cssFilePath = defaultCssFilePath;
|
|
367
399
|
}
|
|
368
|
-
projectConfig.projectApiToken = projectApiToken;
|
|
369
400
|
// plasmic.lock
|
|
370
401
|
const projectLock = config_utils_1.getOrAddProjectLock(context, projectConfig.projectId);
|
|
371
402
|
projectLock.version = version;
|
|
@@ -414,6 +445,7 @@ function syncProjectConfig(context, projectBundle, projectApiToken, version, dep
|
|
|
414
445
|
projectConfig.jsBundleThemes.length === 0) {
|
|
415
446
|
delete projectConfig.jsBundleThemes;
|
|
416
447
|
}
|
|
448
|
+
yield sync_global_contexts_1.syncGlobalContexts(context, projectBundle, projectConfig, projectLock, checksums, baseDir);
|
|
417
449
|
// Write out components
|
|
418
450
|
yield sync_components_1.syncProjectComponents(context, projectConfig, version, componentBundles, forceOverwrite, appendJsxOnMissingBase, summary, pendingMerge, projectLock, checksums, baseDir);
|
|
419
451
|
});
|
package/dist/api.d.ts
CHANGED
|
@@ -25,6 +25,10 @@ export interface GlobalVariantBundle {
|
|
|
25
25
|
contextModule: string;
|
|
26
26
|
contextFileName: string;
|
|
27
27
|
}
|
|
28
|
+
export interface GlobalContextBundle {
|
|
29
|
+
id: string;
|
|
30
|
+
contextModule: string;
|
|
31
|
+
}
|
|
28
32
|
export interface JsBundleTheme {
|
|
29
33
|
themeFileName: string;
|
|
30
34
|
themeModule: string;
|
|
@@ -36,6 +40,7 @@ export interface ProjectMetaBundle {
|
|
|
36
40
|
cssFileName: string;
|
|
37
41
|
cssRules: string;
|
|
38
42
|
jsBundleThemes?: JsBundleTheme[];
|
|
43
|
+
globalContextBundle?: GlobalContextBundle;
|
|
39
44
|
}
|
|
40
45
|
export interface IconBundle {
|
|
41
46
|
id: string;
|
|
@@ -80,6 +85,8 @@ export interface ProjectBundle {
|
|
|
80
85
|
iconAssets: IconBundle[];
|
|
81
86
|
imageAssets: ImageBundle[];
|
|
82
87
|
checksums: ChecksumBundle;
|
|
88
|
+
usedNpmPackages: string[];
|
|
89
|
+
externalCssImports: string[];
|
|
83
90
|
}
|
|
84
91
|
export declare type ProjectMeta = Omit<ProjectBundle, "projectConfig">;
|
|
85
92
|
export interface StyleConfigResponse {
|
|
@@ -109,6 +116,7 @@ export interface ChecksumBundle {
|
|
|
109
116
|
iconChecksums: Array<[string, string]>;
|
|
110
117
|
globalVariantChecksums: Array<[string, string]>;
|
|
111
118
|
projectCssChecksum: string;
|
|
119
|
+
globalContextsChecksum: string;
|
|
112
120
|
}
|
|
113
121
|
export interface CodeComponentMeta {
|
|
114
122
|
id: string;
|
|
@@ -125,6 +133,7 @@ export interface ProjectIdAndToken {
|
|
|
125
133
|
}
|
|
126
134
|
export declare class PlasmicApi {
|
|
127
135
|
private auth;
|
|
136
|
+
private codegenVersion?;
|
|
128
137
|
constructor(auth: AuthConfig);
|
|
129
138
|
genStyleConfig(styleOpts?: StyleConfig): Promise<StyleConfigResponse>;
|
|
130
139
|
/**
|
|
@@ -144,6 +153,7 @@ export declare class PlasmicApi {
|
|
|
144
153
|
}[], recursive?: boolean): Promise<VersionResolution>;
|
|
145
154
|
getCurrentUser(): Promise<import("axios").AxiosResponse<any>>;
|
|
146
155
|
requiredPackages(): Promise<RequiredPackages>;
|
|
156
|
+
latestCodegenVersion(): Promise<string>;
|
|
147
157
|
/**
|
|
148
158
|
* Code-gen endpoint.
|
|
149
159
|
* This will fetch components at an exact specified version.
|
package/dist/api.js
CHANGED
|
@@ -66,6 +66,15 @@ class PlasmicApi {
|
|
|
66
66
|
return Object.assign({}, resp.data);
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
|
+
latestCodegenVersion() {
|
|
70
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
if (!this.codegenVersion) {
|
|
72
|
+
const resp = yield this.post(`${this.codegenHost}/api/v1/code/latest-codegen-version`);
|
|
73
|
+
this.codegenVersion = resp.data;
|
|
74
|
+
}
|
|
75
|
+
return this.codegenVersion;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
69
78
|
/**
|
|
70
79
|
* Code-gen endpoint.
|
|
71
80
|
* This will fetch components at an exact specified version.
|
package/dist/index.js
CHANGED
|
File without changes
|
package/dist/plasmic.schema.json
CHANGED
|
@@ -282,6 +282,10 @@
|
|
|
282
282
|
"description": "File location for the project-wide css styles. Relative to srcDir",
|
|
283
283
|
"type": "string"
|
|
284
284
|
},
|
|
285
|
+
"globalContextsFilePath": {
|
|
286
|
+
"description": "File location for the project-wide global contexts. Relative to srcDir",
|
|
287
|
+
"type": "string"
|
|
288
|
+
},
|
|
285
289
|
"icons": {
|
|
286
290
|
"description": "Metadata for each synced icon in this project",
|
|
287
291
|
"items": {
|
|
@@ -326,6 +330,7 @@
|
|
|
326
330
|
"required": [
|
|
327
331
|
"components",
|
|
328
332
|
"cssFilePath",
|
|
333
|
+
"globalContextsFilePath",
|
|
329
334
|
"icons",
|
|
330
335
|
"images",
|
|
331
336
|
"indirect",
|
|
@@ -9,5 +9,7 @@ export declare function standardTestSetup(includeDep?: boolean): void;
|
|
|
9
9
|
export declare function standardTestTeardown(): void;
|
|
10
10
|
export declare function expectProject1Components(): void;
|
|
11
11
|
export declare const project1Config: ProjectConfig;
|
|
12
|
-
export declare function expectProject1PlasmicJson(
|
|
12
|
+
export declare function expectProject1PlasmicJson(optional?: {
|
|
13
|
+
[k in keyof ProjectConfig]?: boolean;
|
|
14
|
+
}): void;
|
|
13
15
|
export declare function expectProjectAndDepPlasmicJson(): void;
|
|
@@ -139,12 +139,15 @@ exports.project1Config = {
|
|
|
139
139
|
images: [],
|
|
140
140
|
jsBundleThemes: [],
|
|
141
141
|
indirect: false,
|
|
142
|
+
globalContextsFilePath: "",
|
|
142
143
|
};
|
|
143
|
-
function expectProject1PlasmicJson() {
|
|
144
|
+
function expectProject1PlasmicJson(optional) {
|
|
144
145
|
const plasmicJson = exports.tmpRepo.readPlasmicJson();
|
|
145
146
|
expect(plasmicJson.projects.length).toEqual(1);
|
|
146
147
|
const projectConfig = plasmicJson.projects[0];
|
|
147
|
-
|
|
148
|
+
if (!(optional === null || optional === void 0 ? void 0 : optional.projectApiToken)) {
|
|
149
|
+
expect(projectConfig.projectApiToken).toBe("abc");
|
|
150
|
+
}
|
|
148
151
|
expect(projectConfig.components.length).toEqual(2);
|
|
149
152
|
const componentNames = projectConfig.components.map((c) => c.name);
|
|
150
153
|
expect(componentNames).toContain("Button");
|
package/dist/utils/checksum.js
CHANGED
|
@@ -13,6 +13,7 @@ function getChecksums(context, opts, projectId, componentIds) {
|
|
|
13
13
|
cssRulesChecksums: [],
|
|
14
14
|
globalVariantChecksums: [],
|
|
15
15
|
projectCssChecksum: "",
|
|
16
|
+
globalContextsChecksum: "",
|
|
16
17
|
};
|
|
17
18
|
}
|
|
18
19
|
const fileLocks = projectLock.fileLocks;
|
|
@@ -51,6 +52,11 @@ function getChecksums(context, opts, projectId, componentIds) {
|
|
|
51
52
|
const projectCssChecksums = fileLocks.filter((fileLock) => fileLock.type === "projectCss");
|
|
52
53
|
lang_utils_1.assert(projectCssChecksums.length < 2);
|
|
53
54
|
const projectCssChecksum = projectCssChecksums.length > 0 ? projectCssChecksums[0].checksum : "";
|
|
55
|
+
const globalContextsChecksums = fileLocks.filter((fileLock) => fileLock.type === "globalContexts" && fileLock.assetId === projectId);
|
|
56
|
+
lang_utils_1.assert(globalContextsChecksums.length < 2);
|
|
57
|
+
const globalContextsChecksum = globalContextsChecksums.length > 0
|
|
58
|
+
? globalContextsChecksums[0].checksum
|
|
59
|
+
: "";
|
|
54
60
|
return {
|
|
55
61
|
imageChecksums,
|
|
56
62
|
iconChecksums,
|
|
@@ -58,6 +64,7 @@ function getChecksums(context, opts, projectId, componentIds) {
|
|
|
58
64
|
cssRulesChecksums,
|
|
59
65
|
globalVariantChecksums,
|
|
60
66
|
projectCssChecksum,
|
|
67
|
+
globalContextsChecksum,
|
|
61
68
|
};
|
|
62
69
|
}
|
|
63
70
|
exports.getChecksums = getChecksums;
|
package/dist/utils/code-utils.js
CHANGED
|
@@ -41,9 +41,11 @@ const Prettier = __importStar(require("prettier"));
|
|
|
41
41
|
const prettier_1 = require("prettier");
|
|
42
42
|
const ts = __importStar(require("typescript"));
|
|
43
43
|
const upath_1 = __importDefault(require("upath"));
|
|
44
|
+
const sync_global_contexts_1 = require("../actions/sync-global-contexts");
|
|
44
45
|
const sync_images_1 = require("../actions/sync-images");
|
|
45
46
|
const deps_1 = require("../deps");
|
|
46
47
|
const error_1 = require("../utils/error");
|
|
48
|
+
const config_utils_1 = require("./config-utils");
|
|
47
49
|
const file_utils_1 = require("./file-utils");
|
|
48
50
|
const lang_utils_1 = require("./lang-utils");
|
|
49
51
|
exports.formatAsLocal = (content, filePath, baseDir, defaultOpts = {}) => {
|
|
@@ -341,6 +343,7 @@ function fixAllImportStatements(context, baseDir, summary) {
|
|
|
341
343
|
}
|
|
342
344
|
}
|
|
343
345
|
}
|
|
346
|
+
fixGlobalContextImportStatements(context, fixImportContext, baseDir);
|
|
344
347
|
});
|
|
345
348
|
}
|
|
346
349
|
exports.fixAllImportStatements = fixAllImportStatements;
|
|
@@ -455,3 +458,26 @@ exports.formatScript = (code, baseDir) => {
|
|
|
455
458
|
useTabs: false,
|
|
456
459
|
});
|
|
457
460
|
};
|
|
461
|
+
function fixGlobalContextImportStatements(context, fixImportContext, baseDir) {
|
|
462
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
463
|
+
for (const project of context.config.projects) {
|
|
464
|
+
if (!project.globalContextsFilePath)
|
|
465
|
+
continue;
|
|
466
|
+
const resourcePath = sync_global_contexts_1.getGlobalContextsResourcePath(context, project);
|
|
467
|
+
let prevContent;
|
|
468
|
+
try {
|
|
469
|
+
prevContent = file_utils_1.readFileText(file_utils_1.makeFilePath(context, resourcePath)).toString();
|
|
470
|
+
}
|
|
471
|
+
catch (e) {
|
|
472
|
+
deps_1.logger.warn(`${resourcePath} is missing. If you deleted this component, remember to remove the component from ${config_utils_1.CONFIG_FILE_NAME}`);
|
|
473
|
+
throw e;
|
|
474
|
+
}
|
|
475
|
+
const newContent = replaceImports(context, prevContent, resourcePath, fixImportContext, false, baseDir, true);
|
|
476
|
+
if (prevContent !== newContent) {
|
|
477
|
+
yield file_utils_1.writeFileContent(context, resourcePath, newContent, {
|
|
478
|
+
force: true,
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
}
|
|
@@ -118,6 +118,8 @@ export interface ProjectConfig {
|
|
|
118
118
|
version: string;
|
|
119
119
|
/** File location for the project-wide css styles. Relative to srcDir */
|
|
120
120
|
cssFilePath: string;
|
|
121
|
+
/** File location for the project-wide global contexts. Relative to srcDir */
|
|
122
|
+
globalContextsFilePath: string;
|
|
121
123
|
jsBundleThemes?: JsBundleThemeConfig[];
|
|
122
124
|
codeComponents?: CodeComponentConfig[];
|
|
123
125
|
/** Metadata for each synced component in this project. */
|
|
@@ -213,7 +215,7 @@ export interface GlobalVariantGroupConfig {
|
|
|
213
215
|
contextFilePath: string;
|
|
214
216
|
}
|
|
215
217
|
export interface FileLock {
|
|
216
|
-
type: "renderModule" | "cssRules" | "icon" | "image" | "projectCss" | "globalVariant";
|
|
218
|
+
type: "renderModule" | "cssRules" | "icon" | "image" | "projectCss" | "globalVariant" | "globalContexts";
|
|
217
219
|
checksum: string;
|
|
218
220
|
assetId: string;
|
|
219
221
|
}
|
|
@@ -225,6 +227,7 @@ export interface ProjectLock {
|
|
|
225
227
|
};
|
|
226
228
|
lang: "ts" | "js";
|
|
227
229
|
fileLocks: FileLock[];
|
|
230
|
+
codegenVersion?: string;
|
|
228
231
|
}
|
|
229
232
|
export interface PlasmicLock {
|
|
230
233
|
projects: ProjectLock[];
|
|
@@ -40,6 +40,7 @@ function createProjectConfig(base) {
|
|
|
40
40
|
icons: [],
|
|
41
41
|
images: [],
|
|
42
42
|
indirect: base.indirect,
|
|
43
|
+
globalContextsFilePath: "",
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
46
|
exports.createProjectConfig = createProjectConfig;
|
|
@@ -150,6 +151,7 @@ function getOrAddProjectConfig(context, projectId, base // if one doesn't exist,
|
|
|
150
151
|
images: [],
|
|
151
152
|
jsBundleThemes: [],
|
|
152
153
|
indirect: false,
|
|
154
|
+
globalContextsFilePath: "",
|
|
153
155
|
};
|
|
154
156
|
context.config.projects.push(project);
|
|
155
157
|
}
|
package/dist/utils/file-utils.js
CHANGED
|
@@ -81,10 +81,7 @@ function removeMissingFilesFromLock(context, config, lock) {
|
|
|
81
81
|
image.id,
|
|
82
82
|
image,
|
|
83
83
|
]));
|
|
84
|
-
const knownIcons = Object.fromEntries(knownProjects[project.projectId].icons.map((icons) => [
|
|
85
|
-
icons.id,
|
|
86
|
-
icons,
|
|
87
|
-
]));
|
|
84
|
+
const knownIcons = Object.fromEntries(knownProjects[project.projectId].icons.map((icons) => [icons.id, icons]));
|
|
88
85
|
project.fileLocks = project.fileLocks.filter((lock) => {
|
|
89
86
|
switch (lock.type) {
|
|
90
87
|
default:
|
|
@@ -100,6 +97,8 @@ function removeMissingFilesFromLock(context, config, lock) {
|
|
|
100
97
|
return knownImages[lock.assetId];
|
|
101
98
|
case "icon":
|
|
102
99
|
return knownIcons[lock.assetId];
|
|
100
|
+
case "globalContexts":
|
|
101
|
+
return knownProjects[project.projectId].globalContextsFilePath;
|
|
103
102
|
}
|
|
104
103
|
});
|
|
105
104
|
return project;
|
|
@@ -169,6 +168,11 @@ function resolveMissingFilesInConfig(context, config) {
|
|
|
169
168
|
for (const project of config.projects) {
|
|
170
169
|
project.cssFilePath =
|
|
171
170
|
(yield attemptToRestoreFilePath(context, project.cssFilePath, baseNameToFiles)) || "";
|
|
171
|
+
if (!project.globalContextsFilePath) {
|
|
172
|
+
project.globalContextsFilePath = "";
|
|
173
|
+
}
|
|
174
|
+
project.globalContextsFilePath =
|
|
175
|
+
(yield attemptToRestoreFilePath(context, project.globalContextsFilePath, baseNameToFiles)) || "";
|
|
172
176
|
project.images = yield filterFiles(project.images, "filePath");
|
|
173
177
|
project.icons = yield filterFiles(project.icons, "moduleFilePath");
|
|
174
178
|
project.jsBundleThemes = yield filterFiles(project.jsBundleThemes || [], "themeFilePath");
|
|
@@ -75,6 +75,15 @@ function checkProjectMeta(meta, root, context, opts) {
|
|
|
75
75
|
const projectName = meta.projectName;
|
|
76
76
|
const newVersion = meta.version;
|
|
77
77
|
const indirect = meta.indirect;
|
|
78
|
+
// If the codegen version on-disk is invalid, we will sync again the project.
|
|
79
|
+
const checkCodegenVersion = () => __awaiter(this, void 0, void 0, function* () {
|
|
80
|
+
const projectLock = context.lock.projects.find((p) => p.projectId === projectId);
|
|
81
|
+
if (!!(projectLock === null || projectLock === void 0 ? void 0 : projectLock.codegenVersion) && semver.gte(projectLock.codegenVersion, yield context.api.latestCodegenVersion())) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
});
|
|
86
|
+
const isOnDiskCodeInvalid = yield checkCodegenVersion();
|
|
78
87
|
// Checks newVersion against plasmic.lock
|
|
79
88
|
const checkVersionLock = () => __awaiter(this, void 0, void 0, function* () {
|
|
80
89
|
const projectLock = context.lock.projects.find((p) => p.projectId === projectId);
|
|
@@ -88,7 +97,9 @@ function checkProjectMeta(meta, root, context, opts) {
|
|
|
88
97
|
meta !== root) {
|
|
89
98
|
// If this is a dependency (not root), and we're dealing with latest dep version
|
|
90
99
|
// just skip, it's confusing
|
|
91
|
-
|
|
100
|
+
if (!isOnDiskCodeInvalid) {
|
|
101
|
+
deps_1.logger.warn(`'${root.projectName}' depends on ${projectName}@${newVersion}. To update this project, explicitly specify this project for sync. Skipping...`);
|
|
102
|
+
}
|
|
92
103
|
return false;
|
|
93
104
|
}
|
|
94
105
|
if (semver.isLatest(newVersion)) {
|
|
@@ -106,7 +117,9 @@ function checkProjectMeta(meta, root, context, opts) {
|
|
|
106
117
|
return true;
|
|
107
118
|
}
|
|
108
119
|
else {
|
|
109
|
-
|
|
120
|
+
if (!isOnDiskCodeInvalid) {
|
|
121
|
+
deps_1.logger.info(`Project '${projectName}'@${newVersion} is already up to date; skipping. (To force an update, run again with "--force")`);
|
|
122
|
+
}
|
|
110
123
|
return false;
|
|
111
124
|
}
|
|
112
125
|
}
|
|
@@ -164,9 +177,24 @@ function checkProjectMeta(meta, root, context, opts) {
|
|
|
164
177
|
// we should always sync it, even if nothing has changed
|
|
165
178
|
return true;
|
|
166
179
|
}
|
|
167
|
-
|
|
180
|
+
const checkedVersion = (yield checkVersionLock()) &&
|
|
168
181
|
(yield checkVersionRange()) &&
|
|
169
|
-
(yield checkIndirect())
|
|
182
|
+
(yield checkIndirect());
|
|
183
|
+
if (!checkedVersion && isOnDiskCodeInvalid) {
|
|
184
|
+
// sync, but try to keep the current version on disk
|
|
185
|
+
const projectLock = context.lock.projects.find((p) => p.projectId === projectId);
|
|
186
|
+
const versionOnDisk = projectLock === null || projectLock === void 0 ? void 0 : projectLock.version;
|
|
187
|
+
deps_1.logger.warn(`Project '${projectName}' was synced by an incompatible version of Plasmic Codegen. Syncing again on the same version ${projectName}@${versionOnDisk}`);
|
|
188
|
+
meta.version = versionOnDisk !== null && versionOnDisk !== void 0 ? versionOnDisk : meta.version;
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
else if (checkedVersion) {
|
|
192
|
+
// sync and upgrade the version
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
170
198
|
});
|
|
171
199
|
}
|
|
172
200
|
/**
|
package/package.json
CHANGED
package/src/__mocks__/api.ts
CHANGED
|
@@ -339,7 +339,10 @@ class PlasmicApi {
|
|
|
339
339
|
iconChecksums: [],
|
|
340
340
|
globalVariantChecksums: [],
|
|
341
341
|
projectCssChecksum: "",
|
|
342
|
+
globalContextsChecksum: "",
|
|
342
343
|
} as ChecksumBundle,
|
|
344
|
+
usedNpmPackages: [],
|
|
345
|
+
externalCssImports: [],
|
|
343
346
|
};
|
|
344
347
|
return result;
|
|
345
348
|
}
|
|
@@ -370,6 +373,10 @@ class PlasmicApi {
|
|
|
370
373
|
throw new Error("Unimplemented");
|
|
371
374
|
}
|
|
372
375
|
|
|
376
|
+
async latestCodegenVersion(): Promise<string> {
|
|
377
|
+
return "0.0.1";
|
|
378
|
+
}
|
|
379
|
+
|
|
373
380
|
async requiredPackages(): Promise<RequiredPackages> {
|
|
374
381
|
return {
|
|
375
382
|
"@plasmicapp/loader": "0.0.1",
|
|
@@ -71,11 +71,13 @@ describe("Project API tokens", () => {
|
|
|
71
71
|
|
|
72
72
|
expectProject1Components();
|
|
73
73
|
|
|
74
|
-
expectProject1PlasmicJson();
|
|
74
|
+
expectProject1PlasmicJson({ projectApiToken: true });
|
|
75
75
|
|
|
76
76
|
// Re-run, this time with no auth.
|
|
77
77
|
removeAuth();
|
|
78
|
-
await expect(sync(opts)).
|
|
78
|
+
await expect(sync(opts)).rejects.toThrow(
|
|
79
|
+
"No user+token, and project API tokens don't match"
|
|
80
|
+
);
|
|
79
81
|
});
|
|
80
82
|
|
|
81
83
|
test("is filled in by auth'd user if project exists but token was initially missing", async () => {
|
|
@@ -88,11 +90,13 @@ describe("Project API tokens", () => {
|
|
|
88
90
|
|
|
89
91
|
expectProject1Components();
|
|
90
92
|
|
|
91
|
-
expectProject1PlasmicJson();
|
|
93
|
+
expectProject1PlasmicJson({ projectApiToken: true });
|
|
92
94
|
|
|
93
95
|
// Re-run, this time with no auth.
|
|
94
96
|
removeAuth();
|
|
95
|
-
await expect(sync(opts)).
|
|
97
|
+
await expect(sync(opts)).rejects.toThrow(
|
|
98
|
+
"Unable to authenticate Plasmic. Please run 'plasmic auth' or check the projectApiTokens in your plasmic.json, and try again."
|
|
99
|
+
);
|
|
96
100
|
});
|
|
97
101
|
|
|
98
102
|
test("when not available, should prompt for auth", async () => {
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { ChecksumBundle, ProjectMetaBundle } from "../api";
|
|
2
|
+
import { logger } from "../deps";
|
|
3
|
+
import { formatScript, tsxToJsx } from "../utils/code-utils";
|
|
4
|
+
import L from "lodash";
|
|
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 = "PlasmicGlobalContextsProvider";
|
|
18
|
+
|
|
19
|
+
export async function syncGlobalContexts(
|
|
20
|
+
context: PlasmicContext,
|
|
21
|
+
projectMeta: ProjectMetaBundle,
|
|
22
|
+
projectConfig: ProjectConfig,
|
|
23
|
+
projectLock: ProjectLock,
|
|
24
|
+
checksums: ChecksumBundle,
|
|
25
|
+
baseDir: string
|
|
26
|
+
) {
|
|
27
|
+
const resourcePath = getGlobalContextsResourcePath(context, projectConfig);
|
|
28
|
+
if (checksums.globalContextsChecksum && projectMeta.globalContextBundle) {
|
|
29
|
+
if (context.cliArgs.quiet !== true) {
|
|
30
|
+
logger.info(
|
|
31
|
+
`Syncing component: ${COMPONENT_NAME}@${projectLock.version}\t['${projectConfig.projectName}' ${projectConfig.projectId} ${projectConfig.version}]`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
if (context.config.code.lang === "js") {
|
|
35
|
+
projectMeta.globalContextBundle.contextModule = formatScript(
|
|
36
|
+
tsxToJsx(projectMeta.globalContextBundle.contextModule),
|
|
37
|
+
baseDir
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
writeFileContent(
|
|
41
|
+
context,
|
|
42
|
+
resourcePath,
|
|
43
|
+
projectMeta.globalContextBundle.contextModule,
|
|
44
|
+
{ force: false }
|
|
45
|
+
);
|
|
46
|
+
projectConfig.globalContextsFilePath = resourcePath;
|
|
47
|
+
const fl = projectLock.fileLocks.find(
|
|
48
|
+
(fl) =>
|
|
49
|
+
fl.assetId === projectConfig.projectId && fl.type === "globalContexts"
|
|
50
|
+
);
|
|
51
|
+
if (fl) {
|
|
52
|
+
fl.checksum = checksums.globalContextsChecksum;
|
|
53
|
+
} else {
|
|
54
|
+
projectLock.fileLocks.push({
|
|
55
|
+
assetId: projectConfig.projectId,
|
|
56
|
+
checksum: checksums.globalContextsChecksum,
|
|
57
|
+
type: "globalContexts",
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
} else if (
|
|
61
|
+
!checksums.globalContextsChecksum &&
|
|
62
|
+
!projectMeta.globalContextBundle
|
|
63
|
+
) {
|
|
64
|
+
if (fileExists(context, resourcePath)) {
|
|
65
|
+
deleteFile(context, resourcePath);
|
|
66
|
+
}
|
|
67
|
+
projectConfig.globalContextsFilePath = "";
|
|
68
|
+
L.remove(
|
|
69
|
+
projectLock.fileLocks,
|
|
70
|
+
(fl) =>
|
|
71
|
+
fl.assetId === projectConfig.projectId && fl.type === "globalContexts"
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function getGlobalContextsResourcePath(
|
|
77
|
+
context: PlasmicContext,
|
|
78
|
+
projectConfig: ProjectConfig
|
|
79
|
+
) {
|
|
80
|
+
return projectConfig.globalContextsFilePath !== ""
|
|
81
|
+
? projectConfig.globalContextsFilePath
|
|
82
|
+
: defaultResourcePath(
|
|
83
|
+
context,
|
|
84
|
+
projectConfig,
|
|
85
|
+
`${COMPONENT_NAME}.${context.config.code.lang === "ts" ? "tsx" : "jsx"}`
|
|
86
|
+
);
|
|
87
|
+
}
|
package/src/actions/sync.ts
CHANGED
|
@@ -64,6 +64,7 @@ import { syncGlobalVariants } from "./sync-global-variants";
|
|
|
64
64
|
import { syncProjectIconAssets } from "./sync-icons";
|
|
65
65
|
import { syncProjectImageAssets } from "./sync-images";
|
|
66
66
|
import { upsertStyleTokens } from "./sync-styles";
|
|
67
|
+
import { syncGlobalContexts } from "./sync-global-contexts";
|
|
67
68
|
|
|
68
69
|
export interface SyncArgs extends CommonArgs {
|
|
69
70
|
projects: readonly string[];
|
|
@@ -315,6 +316,8 @@ export async function sync(
|
|
|
315
316
|
].map((p) => L.pick(p, "projectId", "projectApiToken"));
|
|
316
317
|
|
|
317
318
|
context.api.attachProjectIdsAndTokens(projectIdsAndTokens);
|
|
319
|
+
const externalNpmPackages = new Set<string>();
|
|
320
|
+
const externalCssImports = new Set<string>();
|
|
318
321
|
|
|
319
322
|
// Perform the actual sync
|
|
320
323
|
await withBufferedFs(async () => {
|
|
@@ -332,6 +335,8 @@ export async function sync(
|
|
|
332
335
|
summary,
|
|
333
336
|
pendingMerge,
|
|
334
337
|
projectMeta.indirect,
|
|
338
|
+
externalNpmPackages,
|
|
339
|
+
externalCssImports,
|
|
335
340
|
metadataDefaults
|
|
336
341
|
);
|
|
337
342
|
}
|
|
@@ -412,10 +417,36 @@ export async function sync(
|
|
|
412
417
|
writeLoaderConfig(opts, config);
|
|
413
418
|
}
|
|
414
419
|
|
|
420
|
+
const codegenVersion = await context.api.latestCodegenVersion();
|
|
421
|
+
context.lock.projects.forEach((p) => {
|
|
422
|
+
if (
|
|
423
|
+
projectsToSync.some(
|
|
424
|
+
(syncedProject) => syncedProject.projectId === p.projectId
|
|
425
|
+
)
|
|
426
|
+
) {
|
|
427
|
+
p.codegenVersion = codegenVersion;
|
|
428
|
+
}
|
|
429
|
+
});
|
|
415
430
|
// Write the new ComponentConfigs to disk
|
|
416
431
|
await updateConfig(context, context.config, baseDir);
|
|
417
432
|
});
|
|
418
433
|
|
|
434
|
+
await checkExternalPkgs(
|
|
435
|
+
context,
|
|
436
|
+
baseDir,
|
|
437
|
+
opts,
|
|
438
|
+
Array.from(externalNpmPackages.keys())
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
if (!opts.quiet && externalCssImports.size > 0) {
|
|
442
|
+
logger.info(
|
|
443
|
+
`This project uses external packages and styles. Make sure to import the following global CSS: ` +
|
|
444
|
+
Array.from(externalCssImports.keys())
|
|
445
|
+
.map((stmt) => `"${stmt}"`)
|
|
446
|
+
.join(", ")
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
|
|
419
450
|
// Post-sync commands
|
|
420
451
|
if (!opts.ignorePostSync) {
|
|
421
452
|
for (const cmd of context.config.postSyncCommands || []) {
|
|
@@ -430,6 +461,30 @@ export async function sync(
|
|
|
430
461
|
}
|
|
431
462
|
}
|
|
432
463
|
|
|
464
|
+
async function checkExternalPkgs(
|
|
465
|
+
context: PlasmicContext,
|
|
466
|
+
baseDir: string,
|
|
467
|
+
opts: SyncArgs,
|
|
468
|
+
pkgs: string[]
|
|
469
|
+
) {
|
|
470
|
+
const missingPkgs = pkgs.filter((pkg) => {
|
|
471
|
+
const installedPkg = findInstalledVersion(context, baseDir, pkg);
|
|
472
|
+
return !installedPkg;
|
|
473
|
+
});
|
|
474
|
+
if (missingPkgs.length > 0) {
|
|
475
|
+
const upgrade = await confirmWithUser(
|
|
476
|
+
`The following packages aren't installed but are required by some projects, would you like to install them? ${missingPkgs.join(
|
|
477
|
+
", "
|
|
478
|
+
)}`,
|
|
479
|
+
opts.yes
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
if (upgrade) {
|
|
483
|
+
installUpgrade(missingPkgs.join(" "), baseDir);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
433
488
|
function maybeRenamePathExt(
|
|
434
489
|
context: PlasmicContext,
|
|
435
490
|
path: string,
|
|
@@ -480,6 +535,8 @@ async function syncProject(
|
|
|
480
535
|
summary: Map<string, ComponentUpdateSummary>,
|
|
481
536
|
pendingMerge: ComponentPendingMerge[],
|
|
482
537
|
indirect: boolean,
|
|
538
|
+
externalNpmPackages: Set<string>,
|
|
539
|
+
externalCssImports: Set<string>,
|
|
483
540
|
metadataDefaults?: Metadata
|
|
484
541
|
): Promise<void> {
|
|
485
542
|
const newComponentScheme =
|
|
@@ -602,6 +659,12 @@ async function syncProject(
|
|
|
602
659
|
projectBundle.imageAssets,
|
|
603
660
|
projectBundle.checksums
|
|
604
661
|
);
|
|
662
|
+
(projectBundle.usedNpmPackages || []).forEach((pkg) =>
|
|
663
|
+
externalNpmPackages.add(pkg)
|
|
664
|
+
);
|
|
665
|
+
(projectBundle.externalCssImports || []).forEach((css) =>
|
|
666
|
+
externalCssImports.add(css)
|
|
667
|
+
);
|
|
605
668
|
}
|
|
606
669
|
|
|
607
670
|
async function syncStyleConfig(
|
|
@@ -662,7 +725,6 @@ async function syncProjectConfig(
|
|
|
662
725
|
if (!projectConfig.cssFilePath) {
|
|
663
726
|
projectConfig.cssFilePath = defaultCssFilePath;
|
|
664
727
|
}
|
|
665
|
-
projectConfig.projectApiToken = projectApiToken;
|
|
666
728
|
|
|
667
729
|
// plasmic.lock
|
|
668
730
|
const projectLock = getOrAddProjectLock(context, projectConfig.projectId);
|
|
@@ -730,6 +792,15 @@ async function syncProjectConfig(
|
|
|
730
792
|
delete projectConfig.jsBundleThemes;
|
|
731
793
|
}
|
|
732
794
|
|
|
795
|
+
await syncGlobalContexts(
|
|
796
|
+
context,
|
|
797
|
+
projectBundle,
|
|
798
|
+
projectConfig,
|
|
799
|
+
projectLock,
|
|
800
|
+
checksums,
|
|
801
|
+
baseDir
|
|
802
|
+
);
|
|
803
|
+
|
|
733
804
|
// Write out components
|
|
734
805
|
await syncProjectComponents(
|
|
735
806
|
context,
|
package/src/api.ts
CHANGED
|
@@ -39,6 +39,11 @@ export interface GlobalVariantBundle {
|
|
|
39
39
|
contextFileName: string;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
export interface GlobalContextBundle {
|
|
43
|
+
id: string;
|
|
44
|
+
contextModule: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
42
47
|
export interface JsBundleTheme {
|
|
43
48
|
themeFileName: string;
|
|
44
49
|
themeModule: string;
|
|
@@ -51,6 +56,7 @@ export interface ProjectMetaBundle {
|
|
|
51
56
|
cssFileName: string;
|
|
52
57
|
cssRules: string;
|
|
53
58
|
jsBundleThemes?: JsBundleTheme[];
|
|
59
|
+
globalContextBundle?: GlobalContextBundle;
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
export interface IconBundle {
|
|
@@ -101,6 +107,8 @@ export interface ProjectBundle {
|
|
|
101
107
|
iconAssets: IconBundle[];
|
|
102
108
|
imageAssets: ImageBundle[];
|
|
103
109
|
checksums: ChecksumBundle;
|
|
110
|
+
usedNpmPackages: string[];
|
|
111
|
+
externalCssImports: string[];
|
|
104
112
|
}
|
|
105
113
|
|
|
106
114
|
export type ProjectMeta = Omit<ProjectBundle, "projectConfig">;
|
|
@@ -140,6 +148,8 @@ export interface ChecksumBundle {
|
|
|
140
148
|
globalVariantChecksums: Array<[string, string]>;
|
|
141
149
|
// Checksum of projectCss file
|
|
142
150
|
projectCssChecksum: string;
|
|
151
|
+
// Checksum of project global contexts
|
|
152
|
+
globalContextsChecksum: string;
|
|
143
153
|
}
|
|
144
154
|
|
|
145
155
|
export interface CodeComponentMeta {
|
|
@@ -159,6 +169,7 @@ export interface ProjectIdAndToken {
|
|
|
159
169
|
}
|
|
160
170
|
|
|
161
171
|
export class PlasmicApi {
|
|
172
|
+
private codegenVersion?: string;
|
|
162
173
|
constructor(private auth: AuthConfig) {}
|
|
163
174
|
|
|
164
175
|
async genStyleConfig(styleOpts?: StyleConfig): Promise<StyleConfigResponse> {
|
|
@@ -211,6 +222,16 @@ export class PlasmicApi {
|
|
|
211
222
|
return { ...resp.data } as RequiredPackages;
|
|
212
223
|
}
|
|
213
224
|
|
|
225
|
+
async latestCodegenVersion(): Promise<string> {
|
|
226
|
+
if (!this.codegenVersion) {
|
|
227
|
+
const resp = await this.post(
|
|
228
|
+
`${this.codegenHost}/api/v1/code/latest-codegen-version`
|
|
229
|
+
);
|
|
230
|
+
this.codegenVersion = resp.data as string;
|
|
231
|
+
}
|
|
232
|
+
return this.codegenVersion;
|
|
233
|
+
}
|
|
234
|
+
|
|
214
235
|
/**
|
|
215
236
|
* Code-gen endpoint.
|
|
216
237
|
* This will fetch components at an exact specified version.
|
|
@@ -148,13 +148,18 @@ export const project1Config: ProjectConfig = {
|
|
|
148
148
|
images: [],
|
|
149
149
|
jsBundleThemes: [],
|
|
150
150
|
indirect: false,
|
|
151
|
+
globalContextsFilePath: "",
|
|
151
152
|
};
|
|
152
153
|
|
|
153
|
-
export function expectProject1PlasmicJson(
|
|
154
|
+
export function expectProject1PlasmicJson(
|
|
155
|
+
optional?: { [k in keyof ProjectConfig]?: boolean }
|
|
156
|
+
) {
|
|
154
157
|
const plasmicJson = tmpRepo.readPlasmicJson();
|
|
155
158
|
expect(plasmicJson.projects.length).toEqual(1);
|
|
156
159
|
const projectConfig = plasmicJson.projects[0];
|
|
157
|
-
|
|
160
|
+
if (!optional?.projectApiToken) {
|
|
161
|
+
expect(projectConfig.projectApiToken).toBe("abc");
|
|
162
|
+
}
|
|
158
163
|
expect(projectConfig.components.length).toEqual(2);
|
|
159
164
|
const componentNames = projectConfig.components.map((c) => c.name);
|
|
160
165
|
expect(componentNames).toContain("Button");
|
package/src/utils/checksum.ts
CHANGED
|
@@ -25,6 +25,7 @@ export function getChecksums(
|
|
|
25
25
|
cssRulesChecksums: [],
|
|
26
26
|
globalVariantChecksums: [],
|
|
27
27
|
projectCssChecksum: "",
|
|
28
|
+
globalContextsChecksum: "",
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -95,6 +96,16 @@ export function getChecksums(
|
|
|
95
96
|
const projectCssChecksum =
|
|
96
97
|
projectCssChecksums.length > 0 ? projectCssChecksums[0].checksum : "";
|
|
97
98
|
|
|
99
|
+
const globalContextsChecksums = fileLocks.filter(
|
|
100
|
+
(fileLock) =>
|
|
101
|
+
fileLock.type === "globalContexts" && fileLock.assetId === projectId
|
|
102
|
+
);
|
|
103
|
+
assert(globalContextsChecksums.length < 2);
|
|
104
|
+
const globalContextsChecksum =
|
|
105
|
+
globalContextsChecksums.length > 0
|
|
106
|
+
? globalContextsChecksums[0].checksum
|
|
107
|
+
: "";
|
|
108
|
+
|
|
98
109
|
return {
|
|
99
110
|
imageChecksums,
|
|
100
111
|
iconChecksums,
|
|
@@ -102,5 +113,6 @@ export function getChecksums(
|
|
|
102
113
|
cssRulesChecksums,
|
|
103
114
|
globalVariantChecksums,
|
|
104
115
|
projectCssChecksum,
|
|
116
|
+
globalContextsChecksum,
|
|
105
117
|
};
|
|
106
118
|
}
|
package/src/utils/code-utils.ts
CHANGED
|
@@ -8,6 +8,7 @@ import * as Prettier from "prettier";
|
|
|
8
8
|
import { Options, resolveConfig } from "prettier";
|
|
9
9
|
import * as ts from "typescript";
|
|
10
10
|
import path from "upath";
|
|
11
|
+
import { getGlobalContextsResourcePath } from "../actions/sync-global-contexts";
|
|
11
12
|
import {
|
|
12
13
|
fixComponentCssReferences,
|
|
13
14
|
fixComponentImagesReferences,
|
|
@@ -17,6 +18,7 @@ import { HandledError } from "../utils/error";
|
|
|
17
18
|
import {
|
|
18
19
|
CodeComponentConfig,
|
|
19
20
|
ComponentConfig,
|
|
21
|
+
CONFIG_FILE_NAME,
|
|
20
22
|
GlobalVariantGroupConfig,
|
|
21
23
|
IconConfig,
|
|
22
24
|
ImageConfig,
|
|
@@ -473,6 +475,7 @@ export async function fixAllImportStatements(
|
|
|
473
475
|
}
|
|
474
476
|
}
|
|
475
477
|
}
|
|
478
|
+
fixGlobalContextImportStatements(context, fixImportContext, baseDir);
|
|
476
479
|
}
|
|
477
480
|
|
|
478
481
|
async function fixComponentImportStatements(
|
|
@@ -654,3 +657,42 @@ export const formatScript = (code: string, baseDir: string) => {
|
|
|
654
657
|
useTabs: false,
|
|
655
658
|
});
|
|
656
659
|
};
|
|
660
|
+
|
|
661
|
+
async function fixGlobalContextImportStatements(
|
|
662
|
+
context: PlasmicContext,
|
|
663
|
+
fixImportContext: FixImportContext,
|
|
664
|
+
baseDir: string
|
|
665
|
+
) {
|
|
666
|
+
for (const project of context.config.projects) {
|
|
667
|
+
if (!project.globalContextsFilePath) continue;
|
|
668
|
+
const resourcePath = getGlobalContextsResourcePath(context, project);
|
|
669
|
+
|
|
670
|
+
let prevContent: string;
|
|
671
|
+
try {
|
|
672
|
+
prevContent = readFileText(
|
|
673
|
+
makeFilePath(context, resourcePath)
|
|
674
|
+
).toString();
|
|
675
|
+
} catch (e) {
|
|
676
|
+
logger.warn(
|
|
677
|
+
`${resourcePath} is missing. If you deleted this component, remember to remove the component from ${CONFIG_FILE_NAME}`
|
|
678
|
+
);
|
|
679
|
+
throw e;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const newContent = replaceImports(
|
|
683
|
+
context,
|
|
684
|
+
prevContent,
|
|
685
|
+
resourcePath,
|
|
686
|
+
fixImportContext,
|
|
687
|
+
false,
|
|
688
|
+
baseDir,
|
|
689
|
+
true
|
|
690
|
+
);
|
|
691
|
+
|
|
692
|
+
if (prevContent !== newContent) {
|
|
693
|
+
await writeFileContent(context, resourcePath, newContent, {
|
|
694
|
+
force: true,
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
@@ -159,6 +159,8 @@ export interface ProjectConfig {
|
|
|
159
159
|
version: string;
|
|
160
160
|
/** File location for the project-wide css styles. Relative to srcDir */
|
|
161
161
|
cssFilePath: string;
|
|
162
|
+
/** File location for the project-wide global contexts. Relative to srcDir */
|
|
163
|
+
globalContextsFilePath: string;
|
|
162
164
|
|
|
163
165
|
// Code-component-related fields can be treated as optional not to be shown
|
|
164
166
|
// to the users nor appear to be missing in the documentation.
|
|
@@ -197,6 +199,7 @@ export function createProjectConfig(base: {
|
|
|
197
199
|
icons: [],
|
|
198
200
|
images: [],
|
|
199
201
|
indirect: base.indirect,
|
|
202
|
+
globalContextsFilePath: "",
|
|
200
203
|
};
|
|
201
204
|
}
|
|
202
205
|
|
|
@@ -299,7 +302,8 @@ export interface FileLock {
|
|
|
299
302
|
| "icon"
|
|
300
303
|
| "image"
|
|
301
304
|
| "projectCss"
|
|
302
|
-
| "globalVariant"
|
|
305
|
+
| "globalVariant"
|
|
306
|
+
| "globalContexts";
|
|
303
307
|
// The checksum value for the file
|
|
304
308
|
checksum: string;
|
|
305
309
|
// The component id, or the image asset id
|
|
@@ -318,6 +322,8 @@ export interface ProjectLock {
|
|
|
318
322
|
lang: "ts" | "js";
|
|
319
323
|
// One for each file whose checksum is computed
|
|
320
324
|
fileLocks: FileLock[];
|
|
325
|
+
// The version of Codegen when this project was written
|
|
326
|
+
codegenVersion?: string;
|
|
321
327
|
}
|
|
322
328
|
|
|
323
329
|
export interface PlasmicLock {
|
|
@@ -528,6 +534,7 @@ export function getOrAddProjectConfig(
|
|
|
528
534
|
images: [],
|
|
529
535
|
jsBundleThemes: [],
|
|
530
536
|
indirect: false,
|
|
537
|
+
globalContextsFilePath: "",
|
|
531
538
|
};
|
|
532
539
|
context.config.projects.push(project);
|
|
533
540
|
}
|
package/src/utils/file-utils.ts
CHANGED
|
@@ -242,6 +242,7 @@ function getAllPaths(context: PlasmicContext): BundleKeyPair[] {
|
|
|
242
242
|
|
|
243
243
|
const pushProject = (proj: ProjectConfig) => {
|
|
244
244
|
pushPath(proj, "cssFilePath");
|
|
245
|
+
pushPath(proj, "globalContextsFilePath");
|
|
245
246
|
for (const component of proj.components) {
|
|
246
247
|
pushComponent(component);
|
|
247
248
|
}
|
|
@@ -282,7 +283,10 @@ function getAllPaths(context: PlasmicContext): BundleKeyPair[] {
|
|
|
282
283
|
* Fixes all src-relative file paths in PlasmicConfig by detecting file
|
|
283
284
|
* movement on disk.
|
|
284
285
|
*/
|
|
285
|
-
export async function fixAllFilePaths(
|
|
286
|
+
export async function fixAllFilePaths(
|
|
287
|
+
context: PlasmicContext,
|
|
288
|
+
baseDir: string
|
|
289
|
+
) {
|
|
286
290
|
const baseNameToFiles = buildBaseNameToFiles(context);
|
|
287
291
|
let changed = false;
|
|
288
292
|
|
package/src/utils/get-context.ts
CHANGED
|
@@ -79,10 +79,7 @@ function removeMissingFilesFromLock(
|
|
|
79
79
|
])
|
|
80
80
|
);
|
|
81
81
|
const knownIcons = Object.fromEntries(
|
|
82
|
-
knownProjects[project.projectId].icons.map((icons) => [
|
|
83
|
-
icons.id,
|
|
84
|
-
icons,
|
|
85
|
-
])
|
|
82
|
+
knownProjects[project.projectId].icons.map((icons) => [icons.id, icons])
|
|
86
83
|
);
|
|
87
84
|
|
|
88
85
|
project.fileLocks = project.fileLocks.filter((lock) => {
|
|
@@ -100,6 +97,8 @@ function removeMissingFilesFromLock(
|
|
|
100
97
|
return knownImages[lock.assetId];
|
|
101
98
|
case "icon":
|
|
102
99
|
return knownIcons[lock.assetId];
|
|
100
|
+
case "globalContexts":
|
|
101
|
+
return knownProjects[project.projectId].globalContextsFilePath;
|
|
103
102
|
}
|
|
104
103
|
});
|
|
105
104
|
|
|
@@ -209,6 +208,16 @@ async function resolveMissingFilesInConfig(
|
|
|
209
208
|
baseNameToFiles
|
|
210
209
|
)) || "";
|
|
211
210
|
|
|
211
|
+
if (!project.globalContextsFilePath) {
|
|
212
|
+
project.globalContextsFilePath = "";
|
|
213
|
+
}
|
|
214
|
+
project.globalContextsFilePath =
|
|
215
|
+
(await attemptToRestoreFilePath(
|
|
216
|
+
context,
|
|
217
|
+
project.globalContextsFilePath,
|
|
218
|
+
baseNameToFiles
|
|
219
|
+
)) || "";
|
|
220
|
+
|
|
212
221
|
project.images = await filterFiles(project.images, "filePath");
|
|
213
222
|
project.icons = await filterFiles(project.icons, "moduleFilePath");
|
|
214
223
|
project.jsBundleThemes = await filterFiles(
|
|
@@ -68,6 +68,21 @@ async function checkProjectMeta(
|
|
|
68
68
|
const newVersion = meta.version;
|
|
69
69
|
const indirect = meta.indirect;
|
|
70
70
|
|
|
71
|
+
// If the codegen version on-disk is invalid, we will sync again the project.
|
|
72
|
+
const checkCodegenVersion = async (): Promise<boolean> => {
|
|
73
|
+
const projectLock = context.lock.projects.find(
|
|
74
|
+
(p) => p.projectId === projectId
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (!!projectLock?.codegenVersion && semver.gte(projectLock.codegenVersion, await context.api.latestCodegenVersion())) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return true;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const isOnDiskCodeInvalid = await checkCodegenVersion();
|
|
85
|
+
|
|
71
86
|
// Checks newVersion against plasmic.lock
|
|
72
87
|
const checkVersionLock = async (): Promise<boolean> => {
|
|
73
88
|
const projectLock = context.lock.projects.find(
|
|
@@ -87,9 +102,11 @@ async function checkProjectMeta(
|
|
|
87
102
|
) {
|
|
88
103
|
// If this is a dependency (not root), and we're dealing with latest dep version
|
|
89
104
|
// just skip, it's confusing
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
105
|
+
if (!isOnDiskCodeInvalid) {
|
|
106
|
+
logger.warn(
|
|
107
|
+
`'${root.projectName}' depends on ${projectName}@${newVersion}. To update this project, explicitly specify this project for sync. Skipping...`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
93
110
|
return false;
|
|
94
111
|
}
|
|
95
112
|
|
|
@@ -111,9 +128,11 @@ async function checkProjectMeta(
|
|
|
111
128
|
);
|
|
112
129
|
return true;
|
|
113
130
|
} else {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
131
|
+
if (!isOnDiskCodeInvalid) {
|
|
132
|
+
logger.info(
|
|
133
|
+
`Project '${projectName}'@${newVersion} is already up to date; skipping. (To force an update, run again with "--force")`
|
|
134
|
+
);
|
|
135
|
+
}
|
|
117
136
|
return false;
|
|
118
137
|
}
|
|
119
138
|
}
|
|
@@ -211,12 +230,29 @@ async function checkProjectMeta(
|
|
|
211
230
|
// we should always sync it, even if nothing has changed
|
|
212
231
|
return true;
|
|
213
232
|
}
|
|
214
|
-
|
|
215
|
-
return (
|
|
233
|
+
const checkedVersion =
|
|
216
234
|
(await checkVersionLock()) &&
|
|
217
235
|
(await checkVersionRange()) &&
|
|
218
|
-
(await checkIndirect())
|
|
219
|
-
|
|
236
|
+
(await checkIndirect());
|
|
237
|
+
|
|
238
|
+
if(!checkedVersion && isOnDiskCodeInvalid) {
|
|
239
|
+
// sync, but try to keep the current version on disk
|
|
240
|
+
const projectLock = context.lock.projects.find(
|
|
241
|
+
(p) => p.projectId === projectId
|
|
242
|
+
);
|
|
243
|
+
const versionOnDisk = projectLock?.version;
|
|
244
|
+
logger.warn(
|
|
245
|
+
`Project '${projectName}' was synced by an incompatible version of Plasmic Codegen. Syncing again on the same version ${projectName}@${versionOnDisk}`
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
meta.version = versionOnDisk ?? meta.version;
|
|
249
|
+
return true;
|
|
250
|
+
} else if (checkedVersion) {
|
|
251
|
+
// sync and upgrade the version
|
|
252
|
+
return true;
|
|
253
|
+
} else {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
220
256
|
}
|
|
221
257
|
|
|
222
258
|
/**
|