@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.
- package/.eslintrc.js +61 -0
- package/.idea/cli.iml +11 -0
- package/.idea/misc.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/README +16 -0
- package/README.internal +46 -0
- package/README.md +17 -0
- package/build.sh +8 -0
- package/dist/__mocks__/api.d.ts +16 -0
- package/dist/__mocks__/api.js +297 -0
- package/dist/__tests__/code-utils-spec.d.ts +1 -0
- package/dist/__tests__/code-utils-spec.js +838 -0
- package/dist/__tests__/ftue-spec.d.ts +1 -0
- package/dist/__tests__/ftue-spec.js +39 -0
- package/dist/__tests__/project-api-token-spec.d.ts +1 -0
- package/dist/__tests__/project-api-token-spec.js +147 -0
- package/dist/__tests__/versioned-sync-spec.d.ts +1 -0
- package/dist/__tests__/versioned-sync-spec.js +145 -0
- package/dist/actions/auth.d.ts +8 -0
- package/dist/actions/auth.js +47 -0
- package/dist/actions/fix-imports.d.ts +4 -0
- package/dist/actions/fix-imports.js +25 -0
- package/dist/actions/init.d.ts +62 -0
- package/dist/actions/init.js +460 -0
- package/dist/actions/project-token.d.ts +6 -0
- package/dist/actions/project-token.js +42 -0
- package/dist/actions/sync-components.d.ts +10 -0
- package/dist/actions/sync-components.js +242 -0
- package/dist/actions/sync-global-variants.d.ts +3 -0
- package/dist/actions/sync-global-variants.js +89 -0
- package/dist/actions/sync-icons.d.ts +7 -0
- package/dist/actions/sync-icons.js +92 -0
- package/dist/actions/sync-images.d.ts +6 -0
- package/dist/actions/sync-images.js +137 -0
- package/dist/actions/sync-styles.d.ts +3 -0
- package/dist/actions/sync-styles.js +58 -0
- package/dist/actions/sync.d.ts +25 -0
- package/dist/actions/sync.js +417 -0
- package/dist/actions/upload-bundle.d.ts +15 -0
- package/dist/actions/upload-bundle.js +28 -0
- package/dist/actions/watch.d.ts +14 -0
- package/dist/actions/watch.js +90 -0
- package/dist/api.d.ts +182 -0
- package/dist/api.js +202 -0
- package/dist/deps.d.ts +2 -0
- package/dist/deps.js +20 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +247 -0
- package/dist/lib.d.ts +10 -0
- package/dist/lib.js +23 -0
- package/dist/migrations/0.1.110-fileLocks.d.ts +2 -0
- package/dist/migrations/0.1.110-fileLocks.js +15 -0
- package/dist/migrations/0.1.143-ensureImportModuleType.d.ts +2 -0
- package/dist/migrations/0.1.143-ensureImportModuleType.js +12 -0
- package/dist/migrations/0.1.146-addReactRuntime.d.ts +2 -0
- package/dist/migrations/0.1.146-addReactRuntime.js +10 -0
- package/dist/migrations/0.1.27-migrateInit.d.ts +1 -0
- package/dist/migrations/0.1.27-migrateInit.js +8 -0
- package/dist/migrations/0.1.28-tsToTsx.d.ts +3 -0
- package/dist/migrations/0.1.28-tsToTsx.js +33 -0
- package/dist/migrations/0.1.31-ensureProjectIcons.d.ts +2 -0
- package/dist/migrations/0.1.31-ensureProjectIcons.js +12 -0
- package/dist/migrations/0.1.42-ensureVersion.d.ts +2 -0
- package/dist/migrations/0.1.42-ensureVersion.js +12 -0
- package/dist/migrations/0.1.57-ensureJsBundleThemes.d.ts +2 -0
- package/dist/migrations/0.1.57-ensureJsBundleThemes.js +12 -0
- package/dist/migrations/0.1.64-imageFiles.d.ts +2 -0
- package/dist/migrations/0.1.64-imageFiles.js +17 -0
- package/dist/migrations/0.1.95-componentType.d.ts +2 -0
- package/dist/migrations/0.1.95-componentType.js +16 -0
- package/dist/migrations/migrations.d.ts +10 -0
- package/dist/migrations/migrations.js +119 -0
- package/dist/plasmic.schema.json +463 -0
- package/dist/test-common/fixtures.d.ts +13 -0
- package/dist/test-common/fixtures.js +165 -0
- package/dist/tsconfig-transform.json +68 -0
- package/dist/utils/auth-utils.d.ts +31 -0
- package/dist/utils/auth-utils.js +236 -0
- package/dist/utils/checksum.d.ts +4 -0
- package/dist/utils/checksum.js +63 -0
- package/dist/utils/code-utils.d.ts +46 -0
- package/dist/utils/code-utils.js +457 -0
- package/dist/utils/config-utils.d.ts +271 -0
- package/dist/utils/config-utils.js +178 -0
- package/dist/utils/envdetect.d.ts +4 -0
- package/dist/utils/envdetect.js +42 -0
- package/dist/utils/error.d.ts +14 -0
- package/dist/utils/error.js +42 -0
- package/dist/utils/file-utils.d.ts +71 -0
- package/dist/utils/file-utils.js +433 -0
- package/dist/utils/get-context.d.ts +40 -0
- package/dist/utils/get-context.js +339 -0
- package/dist/utils/help.d.ts +2 -0
- package/dist/utils/help.js +56 -0
- package/dist/utils/lang-utils.d.ts +10 -0
- package/dist/utils/lang-utils.js +52 -0
- package/dist/utils/npm-utils.d.ts +28 -0
- package/dist/utils/npm-utils.js +215 -0
- package/dist/utils/prompts.d.ts +6 -0
- package/dist/utils/prompts.js +23 -0
- package/dist/utils/resolve-utils.d.ts +13 -0
- package/dist/utils/resolve-utils.js +198 -0
- package/dist/utils/semver.d.ts +34 -0
- package/dist/utils/semver.js +61 -0
- package/dist/utils/test-utils.d.ts +22 -0
- package/dist/utils/test-utils.js +106 -0
- package/dist/utils/user-utils.d.ts +7 -0
- package/dist/utils/user-utils.js +48 -0
- package/jest.config.js +6 -0
- package/package.json +80 -0
- package/src/__mocks__/api.ts +394 -0
- package/src/__tests__/code-utils-spec.ts +881 -0
- package/src/__tests__/ftue-spec.ts +43 -0
- package/src/__tests__/project-api-token-spec.ts +208 -0
- package/src/__tests__/versioned-sync-spec.ts +176 -0
- package/src/actions/auth.ts +43 -0
- package/src/actions/fix-imports.ts +13 -0
- package/src/actions/init.ts +638 -0
- package/src/actions/project-token.ts +36 -0
- package/src/actions/sync-components.ts +405 -0
- package/src/actions/sync-global-variants.ts +129 -0
- package/src/actions/sync-icons.ts +135 -0
- package/src/actions/sync-images.ts +191 -0
- package/src/actions/sync-styles.ts +71 -0
- package/src/actions/sync.ts +747 -0
- package/src/actions/upload-bundle.ts +38 -0
- package/src/actions/watch.ts +95 -0
- package/src/api.ts +407 -0
- package/src/deps.ts +18 -0
- package/src/index.ts +300 -0
- package/src/lib.ts +10 -0
- package/src/migrations/0.1.110-fileLocks.ts +16 -0
- package/src/migrations/0.1.146-addReactRuntime.ts +8 -0
- package/src/migrations/0.1.27-migrateInit.ts +4 -0
- package/src/migrations/0.1.28-tsToTsx.ts +37 -0
- package/src/migrations/0.1.31-ensureProjectIcons.ts +10 -0
- package/src/migrations/0.1.42-ensureVersion.ts +10 -0
- package/src/migrations/0.1.57-ensureJsBundleThemes.ts +10 -0
- package/src/migrations/0.1.64-imageFiles.ts +15 -0
- package/src/migrations/0.1.95-componentType.ts +14 -0
- package/src/migrations/migrations.ts +147 -0
- package/src/test-common/fixtures.ts +178 -0
- package/src/utils/auth-utils.ts +276 -0
- package/src/utils/checksum.ts +106 -0
- package/src/utils/code-utils.ts +656 -0
- package/src/utils/config-utils.ts +551 -0
- package/src/utils/envdetect.ts +39 -0
- package/src/utils/error.ts +36 -0
- package/src/utils/file-utils.ts +526 -0
- package/src/utils/get-context.ts +451 -0
- package/src/utils/help.ts +75 -0
- package/src/utils/lang-utils.ts +52 -0
- package/src/utils/npm-utils.ts +223 -0
- package/src/utils/prompts.ts +22 -0
- package/src/utils/resolve-utils.ts +245 -0
- package/src/utils/semver.ts +67 -0
- package/src/utils/test-utils.ts +116 -0
- package/src/utils/user-utils.ts +37 -0
- package/testData/fixImports_plasmic.json +66 -0
- package/tsconfig-transform.json +68 -0
- package/tsconfig.json +67 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { spawnSync } from "child_process";
|
|
2
|
+
import glob from "fast-glob";
|
|
3
|
+
import findupSync from "findup-sync";
|
|
4
|
+
import latest from "latest-version";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import semver from "semver";
|
|
7
|
+
import { logger } from "../deps";
|
|
8
|
+
import { PlasmicContext } from "./config-utils";
|
|
9
|
+
import { findFile, readFileText } from "./file-utils";
|
|
10
|
+
import { confirmWithUser } from "./user-utils";
|
|
11
|
+
|
|
12
|
+
export function getParsedCliPackageJson() {
|
|
13
|
+
const packageJson = findupSync("package.json", { cwd: __dirname });
|
|
14
|
+
if (!packageJson) {
|
|
15
|
+
throw new Error(`Cannot find package.json in ancestors of ${__dirname}`);
|
|
16
|
+
}
|
|
17
|
+
return parsePackageJson(packageJson);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getCliVersion() {
|
|
21
|
+
const j = getParsedCliPackageJson();
|
|
22
|
+
return j.version as string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Call this to check if we match the engine policy
|
|
27
|
+
*/
|
|
28
|
+
export function checkEngineStrict(): boolean {
|
|
29
|
+
const pkg = getParsedCliPackageJson();
|
|
30
|
+
const minNodeVersion = pkg?.engines?.node;
|
|
31
|
+
if (!!minNodeVersion && !semver.satisfies(process.version, minNodeVersion)) {
|
|
32
|
+
logger.warn(`Plasmic only works on Node ${minNodeVersion}`);
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getParsedPackageJson() {
|
|
39
|
+
const packageJson = findupSync("package.json");
|
|
40
|
+
if (!packageJson) {
|
|
41
|
+
throw new Error(`Cannot find package.json`);
|
|
42
|
+
}
|
|
43
|
+
return parsePackageJson(packageJson);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// @TODO: is this function still used?
|
|
47
|
+
export async function warnLatest(
|
|
48
|
+
context: PlasmicContext,
|
|
49
|
+
pkg: string,
|
|
50
|
+
baseDir: string,
|
|
51
|
+
msgs: {
|
|
52
|
+
requiredMsg: () => string;
|
|
53
|
+
updateMsg: (curVersion: string, latestVersion: string) => string;
|
|
54
|
+
},
|
|
55
|
+
yes?: boolean
|
|
56
|
+
) {
|
|
57
|
+
const check = await checkVersion(context, pkg);
|
|
58
|
+
if (check.type === "up-to-date") {
|
|
59
|
+
return;
|
|
60
|
+
} else if (check.type === "wrong-npm-registry") {
|
|
61
|
+
logger.warn(
|
|
62
|
+
`${msgs.requiredMsg()} Unable to find this package in your npm registry. Please update this dependency manually.`
|
|
63
|
+
);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (
|
|
68
|
+
await confirmWithUser(
|
|
69
|
+
`${
|
|
70
|
+
check.type === "not-installed"
|
|
71
|
+
? msgs.requiredMsg()
|
|
72
|
+
: msgs.updateMsg(check.current, check.latest)
|
|
73
|
+
} Do you want to ${
|
|
74
|
+
check.type === "not-installed" ? "add" : "update"
|
|
75
|
+
} it now?`,
|
|
76
|
+
yes
|
|
77
|
+
)
|
|
78
|
+
) {
|
|
79
|
+
installUpgrade(pkg, baseDir);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function checkVersion(context: PlasmicContext, pkg: string) {
|
|
84
|
+
// Try to get the latest version from npm
|
|
85
|
+
let last = null;
|
|
86
|
+
try {
|
|
87
|
+
last = await latest(pkg);
|
|
88
|
+
} catch (e) {
|
|
89
|
+
// This is likely because .npmrc is set to a different registry
|
|
90
|
+
return { type: "wrong-npm-registry" } as const;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const cur = findInstalledVersion(context, pkg);
|
|
94
|
+
if (!cur) {
|
|
95
|
+
return { type: "not-installed" } as const;
|
|
96
|
+
}
|
|
97
|
+
if (semver.gt(last, cur)) {
|
|
98
|
+
return {
|
|
99
|
+
type: "obsolete",
|
|
100
|
+
latest: last,
|
|
101
|
+
current: cur,
|
|
102
|
+
} as const;
|
|
103
|
+
}
|
|
104
|
+
return { type: "up-to-date" } as const;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function findInstalledVersion(context: PlasmicContext, pkg: string) {
|
|
108
|
+
const filename = findInstalledPackageJsonFile(context, pkg);
|
|
109
|
+
if (filename) {
|
|
110
|
+
const json = parsePackageJson(filename);
|
|
111
|
+
if (json && json.name === pkg) {
|
|
112
|
+
return json.version as string;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Detects if the cli is globally installed. `rootDir` is the folder
|
|
120
|
+
* where plasmic.json is
|
|
121
|
+
*/
|
|
122
|
+
export function isCliGloballyInstalled(rootDir: string) {
|
|
123
|
+
const packageJsonFile = findPackageJsonPath(rootDir);
|
|
124
|
+
if (!packageJsonFile) {
|
|
125
|
+
// We assume global, as instructions state global and we can't really
|
|
126
|
+
// do better
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
const installedDir = __dirname;
|
|
130
|
+
|
|
131
|
+
// Else, we assume it is local if the installedDir is a subfolder of
|
|
132
|
+
// the root project dir
|
|
133
|
+
return !installedDir.startsWith(path.dirname(packageJsonFile));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function findPackageJsonPath(dir: string) {
|
|
137
|
+
return findFile(dir, (f) => f === "package.json", {
|
|
138
|
+
traverseParents: true,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function findPackageJsonDir(rootDir: string) {
|
|
143
|
+
const filePath = findPackageJsonPath(rootDir);
|
|
144
|
+
return filePath ? path.dirname(filePath) : undefined;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function findInstalledPackageJsonFile(context: PlasmicContext, pkg: string) {
|
|
148
|
+
const packageJsonPath = findPackageJsonPath(context.rootDir);
|
|
149
|
+
const rootDir = packageJsonPath
|
|
150
|
+
? path.dirname(packageJsonPath)
|
|
151
|
+
: context.rootDir;
|
|
152
|
+
const files = glob.sync(`${rootDir}/**/node_modules/${pkg}/package.json`);
|
|
153
|
+
return files.length > 0 ? files[0] : undefined;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function parsePackageJson(path: string) {
|
|
157
|
+
try {
|
|
158
|
+
return JSON.parse(readFileText(path));
|
|
159
|
+
} catch (e) {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function installUpgrade(
|
|
165
|
+
pkg: string,
|
|
166
|
+
baseDir: string,
|
|
167
|
+
opts: { global?: boolean; dev?: boolean } = {}
|
|
168
|
+
) {
|
|
169
|
+
const cmd = installCommand(pkg, baseDir, opts);
|
|
170
|
+
if (!process.env.QUIET) {
|
|
171
|
+
logger.info(cmd);
|
|
172
|
+
}
|
|
173
|
+
const r = spawnSync(cmd, {
|
|
174
|
+
shell: true,
|
|
175
|
+
stdio: process.env.QUIET ? "ignore" : "inherit",
|
|
176
|
+
cwd: baseDir,
|
|
177
|
+
});
|
|
178
|
+
if (r.status === 0) {
|
|
179
|
+
if (!process.env.QUIET) {
|
|
180
|
+
logger.info(`Successfully added ${pkg} dependency.`);
|
|
181
|
+
}
|
|
182
|
+
return true;
|
|
183
|
+
} else {
|
|
184
|
+
logger.warn(
|
|
185
|
+
`Cannot add ${pkg} to your project dependencies. Please add it manually.`
|
|
186
|
+
);
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function installCommand(
|
|
192
|
+
pkg: string,
|
|
193
|
+
baseDir: string,
|
|
194
|
+
opts: { global?: boolean; dev?: boolean } = {}
|
|
195
|
+
) {
|
|
196
|
+
const mgr = detectPackageManager(baseDir);
|
|
197
|
+
if (mgr === "yarn") {
|
|
198
|
+
if (opts.global) {
|
|
199
|
+
return `yarn global add ${pkg}`;
|
|
200
|
+
} else if (opts.dev) {
|
|
201
|
+
return `yarn add --dev --ignore-scripts -W ${pkg}`;
|
|
202
|
+
} else {
|
|
203
|
+
return `yarn add --ignore-scripts -W ${pkg}`;
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
if (opts.global) {
|
|
207
|
+
return `npm install -g ${pkg}`;
|
|
208
|
+
} else if (opts.dev) {
|
|
209
|
+
return `npm install --save-dev --ignore-scripts ${pkg}`;
|
|
210
|
+
} else {
|
|
211
|
+
return `npm install --ignore-scripts ${pkg}`;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function detectPackageManager(baseDir: string) {
|
|
217
|
+
const yarnLock = findupSync("yarn.lock", { cwd: baseDir });
|
|
218
|
+
if (yarnLock) {
|
|
219
|
+
return "yarn";
|
|
220
|
+
} else {
|
|
221
|
+
return "npm";
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import inquirer from "inquirer";
|
|
2
|
+
|
|
3
|
+
export function askChoice<T>(question: {
|
|
4
|
+
message: string;
|
|
5
|
+
choices: T[];
|
|
6
|
+
defaultAnswer: T;
|
|
7
|
+
hidePrompt: boolean;
|
|
8
|
+
}) {
|
|
9
|
+
if (question.hidePrompt) {
|
|
10
|
+
return question.defaultAnswer;
|
|
11
|
+
}
|
|
12
|
+
return inquirer
|
|
13
|
+
.prompt([
|
|
14
|
+
{
|
|
15
|
+
name: "answer",
|
|
16
|
+
type: "list",
|
|
17
|
+
message: question.message,
|
|
18
|
+
choices: question.choices,
|
|
19
|
+
},
|
|
20
|
+
])
|
|
21
|
+
.then((answer) => answer.answer as T);
|
|
22
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import L from "lodash";
|
|
2
|
+
import { SyncArgs } from "../actions/sync";
|
|
3
|
+
import { ProjectVersionMeta, VersionResolution } from "../api";
|
|
4
|
+
import { logger } from "../deps";
|
|
5
|
+
import { CONFIG_FILE_NAME, PlasmicContext } from "./config-utils";
|
|
6
|
+
import { HandledError } from "./error";
|
|
7
|
+
import { ensure } from "./lang-utils";
|
|
8
|
+
import * as semver from "./semver";
|
|
9
|
+
import { confirmWithUser } from "./user-utils";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Starting at the root, do a BFS of the full dependency tree
|
|
13
|
+
* Because ProjectVersionMeta only stores the (projectId, version),
|
|
14
|
+
* we need to search for the full ProjectVersionMeta of dependencies from `available`
|
|
15
|
+
* @param root
|
|
16
|
+
* @param versionResolution
|
|
17
|
+
*/
|
|
18
|
+
function walkDependencyTree(
|
|
19
|
+
root: ProjectVersionMeta,
|
|
20
|
+
available: ProjectVersionMeta[]
|
|
21
|
+
): ProjectVersionMeta[] {
|
|
22
|
+
const queue: ProjectVersionMeta[] = [root];
|
|
23
|
+
const result: ProjectVersionMeta[] = [];
|
|
24
|
+
|
|
25
|
+
const getMeta = (projectId: string, version: string): ProjectVersionMeta => {
|
|
26
|
+
const meta = available.find(
|
|
27
|
+
(m) => m.projectId === projectId && m.version === version
|
|
28
|
+
);
|
|
29
|
+
if (!meta) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Cannot find projectId=${projectId}, version=${version} in the sync resolution results.`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
return meta;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
while (queue.length > 0) {
|
|
38
|
+
const curr = ensure(queue.shift());
|
|
39
|
+
result.push(curr);
|
|
40
|
+
queue.push(
|
|
41
|
+
...L.toPairs(curr.dependencies).map(([projectId, version]) =>
|
|
42
|
+
getMeta(projectId, version)
|
|
43
|
+
)
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* For a given project, check if its compatible with plasmic.json, plasmic.lock, and user
|
|
52
|
+
* @param meta
|
|
53
|
+
* @param context
|
|
54
|
+
*/
|
|
55
|
+
async function checkProjectMeta(
|
|
56
|
+
meta: ProjectVersionMeta,
|
|
57
|
+
root: ProjectVersionMeta,
|
|
58
|
+
context: PlasmicContext,
|
|
59
|
+
opts: SyncArgs
|
|
60
|
+
): Promise<boolean> {
|
|
61
|
+
const projectId = meta.projectId;
|
|
62
|
+
const projectName = meta.projectName;
|
|
63
|
+
const newVersion = meta.version;
|
|
64
|
+
|
|
65
|
+
// Checks newVersion against plasmic.lock
|
|
66
|
+
const checkVersionLock = async (): Promise<boolean> => {
|
|
67
|
+
const projectLock = context.lock.projects.find(
|
|
68
|
+
(p) => p.projectId === projectId
|
|
69
|
+
);
|
|
70
|
+
const versionOnDisk = projectLock?.version;
|
|
71
|
+
|
|
72
|
+
if (!versionOnDisk) {
|
|
73
|
+
// Always sync if we haven't seen sync'ed before
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (
|
|
78
|
+
semver.isLatest(versionOnDisk) &&
|
|
79
|
+
semver.isLatest(newVersion) &&
|
|
80
|
+
meta !== root
|
|
81
|
+
) {
|
|
82
|
+
// If this is a dependency (not root), and we're dealing with latest dep version
|
|
83
|
+
// just skip, it's confusing
|
|
84
|
+
logger.warn(
|
|
85
|
+
`'${root.projectName}' depends on ${projectName}@${newVersion}. To update this project, explicitly specify this project for sync. Skipping...`
|
|
86
|
+
);
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (semver.isLatest(newVersion)) {
|
|
91
|
+
// Always sync when version set to "latest"
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (semver.isLatest(versionOnDisk)) {
|
|
96
|
+
// Explicitly allow downgrades from "latest" to published version
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// At this point, we can assume newVersion is always X.Y.Z (not latest)
|
|
101
|
+
if (semver.eq(newVersion, versionOnDisk)) {
|
|
102
|
+
if (opts.force) {
|
|
103
|
+
logger.info(
|
|
104
|
+
`Project '${projectName}'@${newVersion} is already up to date, but syncing anyway because --force is used`
|
|
105
|
+
);
|
|
106
|
+
return true;
|
|
107
|
+
} else {
|
|
108
|
+
logger.info(
|
|
109
|
+
`Project '${projectName}'@${newVersion} is already up to date; skipping. (To force an update, run again with "--force")`
|
|
110
|
+
);
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (semver.lt(newVersion, versionOnDisk)) {
|
|
116
|
+
meta === root
|
|
117
|
+
? logger.warn(
|
|
118
|
+
`The local version of '${projectName}' (${versionOnDisk}) is higher than requested version @${newVersion}. Plasmic does not support downgrading a project. You should consider updating the version range in ${CONFIG_FILE_NAME}.`
|
|
119
|
+
)
|
|
120
|
+
: logger.warn(
|
|
121
|
+
`'${root.projectName}' uses '${projectName}'@${newVersion}, but your code has '${projectName}'@${versionOnDisk}. You should consider upgrading this dependency in Plasmic Studio.`
|
|
122
|
+
);
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (semver.gt(newVersion, versionOnDisk)) {
|
|
127
|
+
if (meta === root) {
|
|
128
|
+
return true;
|
|
129
|
+
} else {
|
|
130
|
+
logger.info(
|
|
131
|
+
`'${root.projectName}' uses '${projectName}'@${newVersion}, but your code has version ${versionOnDisk}`
|
|
132
|
+
);
|
|
133
|
+
return await confirmWithUser(
|
|
134
|
+
`Do you want to upgrade '${projectName}' to ${newVersion}?`,
|
|
135
|
+
opts.yes
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
throw new Error(
|
|
141
|
+
`Error comparing version=${newVersion} with the version found in plasmic.lock (${versionOnDisk}) for '${projectName}'`
|
|
142
|
+
);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// Checks newVersion against plasmic.json
|
|
146
|
+
const checkVersionRange = async (): Promise<boolean> => {
|
|
147
|
+
const projectConfig = context.config.projects.find(
|
|
148
|
+
(p) => p.projectId === projectId
|
|
149
|
+
);
|
|
150
|
+
const versionRange = projectConfig?.version;
|
|
151
|
+
// If haven't seen this before
|
|
152
|
+
if (!versionRange) {
|
|
153
|
+
// Always sync down dependencies if it's the first time to avoid compile/fix-imports error
|
|
154
|
+
projectId !== root.projectId
|
|
155
|
+
? logger.info(
|
|
156
|
+
`'${root.projectName}' uses '${projectName}', which has never been synced before. We will also sync '${projectName}'@${newVersion}.`
|
|
157
|
+
)
|
|
158
|
+
: logger.info(
|
|
159
|
+
`'${projectName} has never been synced before. Syncing...'`
|
|
160
|
+
);
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// If satisfies range in plasmic.json
|
|
165
|
+
if (semver.satisfies(newVersion, versionRange)) {
|
|
166
|
+
logger.info(`Updating project '${projectName}' to ${newVersion}`);
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
logger.warn(
|
|
171
|
+
`${projectName}@${newVersion} falls outside the range specified in ${CONFIG_FILE_NAME} (${versionRange})\nTip: To avoid this warning in the future, update your ${CONFIG_FILE_NAME}.`
|
|
172
|
+
);
|
|
173
|
+
return await confirmWithUser(
|
|
174
|
+
"Do you want to force it?",
|
|
175
|
+
opts.force || opts.yes,
|
|
176
|
+
"n"
|
|
177
|
+
);
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const projectIds =
|
|
181
|
+
opts.projects.length > 0
|
|
182
|
+
? opts.projects
|
|
183
|
+
: context.config.projects.map((p) => p.projectId);
|
|
184
|
+
|
|
185
|
+
if (projectIds.includes(projectId) && opts.force) {
|
|
186
|
+
// if --force is used, and this is in the list of projects to sync, then
|
|
187
|
+
// we should always sync it, even if nothing has changed
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return (await checkVersionLock()) && (await checkVersionRange());
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Checks the versionResolution with plasmic.json, plasmic.lock, and user prompts
|
|
196
|
+
* to compute which projects should be synced
|
|
197
|
+
* @param versionResolution
|
|
198
|
+
* @param context
|
|
199
|
+
*/
|
|
200
|
+
export async function checkVersionResolution(
|
|
201
|
+
versionResolution: VersionResolution,
|
|
202
|
+
context: PlasmicContext,
|
|
203
|
+
opts: SyncArgs
|
|
204
|
+
): Promise<ProjectVersionMeta[]> {
|
|
205
|
+
// Fail if there's nothing to sync
|
|
206
|
+
if (versionResolution.projects.length <= 0) {
|
|
207
|
+
throw new HandledError(
|
|
208
|
+
`Found nothing to sync. Make sure the projectId and version values are valid in ${CONFIG_FILE_NAME}.`
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const seen: ProjectVersionMeta[] = [];
|
|
213
|
+
const result: ProjectVersionMeta[] = [];
|
|
214
|
+
for (const root of versionResolution.projects) {
|
|
215
|
+
const queue = opts.nonRecursive
|
|
216
|
+
? [root]
|
|
217
|
+
: walkDependencyTree(root, versionResolution.dependencies).reverse();
|
|
218
|
+
for (const m of queue) {
|
|
219
|
+
// If we haven't seen this yet
|
|
220
|
+
if (!seen.find((p) => p.projectId === m.projectId)) {
|
|
221
|
+
if (await checkProjectMeta(m, root, context, opts)) {
|
|
222
|
+
result.push(m);
|
|
223
|
+
}
|
|
224
|
+
seen.push(m);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Ignore repeats
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function getDependencies(
|
|
234
|
+
projectId: string,
|
|
235
|
+
version: string,
|
|
236
|
+
versionResolution: VersionResolution
|
|
237
|
+
) {
|
|
238
|
+
const filterFn = (m: ProjectVersionMeta) =>
|
|
239
|
+
m.projectId === projectId && m.version === version;
|
|
240
|
+
const meta =
|
|
241
|
+
versionResolution.projects.find(filterFn) ??
|
|
242
|
+
versionResolution.dependencies.find(filterFn);
|
|
243
|
+
|
|
244
|
+
return meta?.dependencies;
|
|
245
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import L from "lodash";
|
|
2
|
+
import * as semverlib from "semver";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Wrap `semver` with support for understanding "latest"
|
|
6
|
+
* - "latest" is both a version and a range
|
|
7
|
+
* - "latest" range will match any valid version number
|
|
8
|
+
* - "latest" version will only match "latest" version range
|
|
9
|
+
**/
|
|
10
|
+
|
|
11
|
+
export type Version = semverlib.SemVer | "latest";
|
|
12
|
+
export const latestTag = "latest";
|
|
13
|
+
export const isLatest = (v: string) => v === latestTag;
|
|
14
|
+
export const valid = (v: string) =>
|
|
15
|
+
isLatest(v) ? latestTag : semverlib.valid(v);
|
|
16
|
+
export const inc = (v: string, release: semverlib.ReleaseType) =>
|
|
17
|
+
isLatest(v) ? latestTag : semverlib.inc(v, release);
|
|
18
|
+
export const prerelease = (v: string) =>
|
|
19
|
+
isLatest(v) ? [] : semverlib.prerelease(v);
|
|
20
|
+
export const major = (v: string) =>
|
|
21
|
+
isLatest(v) ? latestTag : semverlib.major(v);
|
|
22
|
+
export const minor = (v: string) =>
|
|
23
|
+
isLatest(v) ? latestTag : semverlib.minor(v);
|
|
24
|
+
export const patch = (v: string) =>
|
|
25
|
+
isLatest(v) ? latestTag : semverlib.patch(v);
|
|
26
|
+
export const eq = (v1: string, v2: string) =>
|
|
27
|
+
(isLatest(v1) && isLatest(v2)) ||
|
|
28
|
+
(!isLatest(v1) && !isLatest(v2) && semverlib.eq(v1, v2));
|
|
29
|
+
export const gt = (v1: string, v2: string) =>
|
|
30
|
+
(isLatest(v1) && !isLatest(v2)) ||
|
|
31
|
+
(!isLatest(v1) && !isLatest(v2) && semverlib.gt(v1, v2));
|
|
32
|
+
export const lt = (v1: string, v2: string) =>
|
|
33
|
+
(!isLatest(v1) && isLatest(v2)) ||
|
|
34
|
+
(!isLatest(v1) && !isLatest(v2) && semverlib.lt(v1, v2));
|
|
35
|
+
export const validRange = (range: string) =>
|
|
36
|
+
isLatest(range) ? latestTag : semverlib.validRange(range);
|
|
37
|
+
export const satisfies = (v: string, range: string) =>
|
|
38
|
+
(isLatest(range) && !!valid(v)) ||
|
|
39
|
+
(!isLatest(v) && !isLatest(range) && semverlib.satisfies(v, range));
|
|
40
|
+
export const toTildeRange = (v: string) =>
|
|
41
|
+
isLatest(v) ? latestTag : !!semverlib.valid(v) ? "~" + v : null;
|
|
42
|
+
export const toCaretRange = (v: string) =>
|
|
43
|
+
isLatest(v) ? latestTag : !!semverlib.valid(v) ? "^" + v : null;
|
|
44
|
+
export const gte = (v1: string, v2: string) => eq(v1, v2) || gt(v1, v2);
|
|
45
|
+
export const lte = (v1: string, v2: string) => eq(v1, v2) || lt(v1, v2);
|
|
46
|
+
export const neq = (v1: string, v2: string) => !eq(v1, v2);
|
|
47
|
+
export const sortAsc = (versions: string[]) =>
|
|
48
|
+
L.cloneDeep(versions).sort((v1, v2) =>
|
|
49
|
+
gt(v1, v2) ? +1 : eq(v1, v2) ? 0 : -1
|
|
50
|
+
);
|
|
51
|
+
export const sortDesc = (versions: string[]) => sortAsc(versions).reverse();
|
|
52
|
+
export const minSatisfying = (versions: string[], range: string) =>
|
|
53
|
+
sortAsc(versions).find((v) => satisfies(v, range)) ?? null;
|
|
54
|
+
export const maxSatisfying = (versions: string[], range: string) =>
|
|
55
|
+
sortDesc(versions).find((v) => satisfies(v, range)) ?? null;
|
|
56
|
+
export const coerce = (v: string) =>
|
|
57
|
+
isLatest(v) ? latestTag : semverlib.coerce(v)?.version;
|
|
58
|
+
export const gtr = (version: string, range: string) =>
|
|
59
|
+
(isLatest(version) && !isLatest(range)) ||
|
|
60
|
+
(!isLatest(version) && !isLatest(range) && semverlib.gtr(version, range));
|
|
61
|
+
export const ltr = (version: string, range: string) =>
|
|
62
|
+
(!isLatest(version) && isLatest(range)) ||
|
|
63
|
+
(!isLatest(version) && !isLatest(range) && semverlib.ltr(version, range));
|
|
64
|
+
export const outside = (version: string, range: string, hilo?: ">" | "<") =>
|
|
65
|
+
(hilo === ">" && gtr(version, range)) ||
|
|
66
|
+
(hilo === "<" && ltr(version, range)) ||
|
|
67
|
+
(!hilo && (gtr(version, range) || ltr(version, range)));
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import * as tmp from "tmp";
|
|
4
|
+
import {
|
|
5
|
+
AuthConfig,
|
|
6
|
+
AUTH_FILE_NAME,
|
|
7
|
+
CONFIG_FILE_NAME,
|
|
8
|
+
LOADER_CONFIG_FILE_NAME,
|
|
9
|
+
PlasmicConfig,
|
|
10
|
+
PlasmicLoaderConfig,
|
|
11
|
+
} from "../utils/config-utils";
|
|
12
|
+
import { deleteFileBuffered, readFileText, writeFileText } from "./file-utils";
|
|
13
|
+
|
|
14
|
+
export class TempRepo {
|
|
15
|
+
tmpDir: tmp.DirResult; // Temporary directory used for tests
|
|
16
|
+
|
|
17
|
+
constructor() {
|
|
18
|
+
this.tmpDir = tmp.dirSync({ unsafeCleanup: true });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
destroy() {
|
|
22
|
+
this.tmpDir.removeCallback();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
resolveFile(relativePath: string): string {
|
|
26
|
+
return path.resolve(this.tmpDir.name, relativePath);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
readFile(relativePath: string): string {
|
|
30
|
+
const absPath = this.resolveFile(relativePath);
|
|
31
|
+
const buf = readFileText(absPath);
|
|
32
|
+
return buf.toString();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
writeFile(relativePath: string, data: string) {
|
|
36
|
+
const absPath = this.resolveFile(relativePath);
|
|
37
|
+
writeFileText(absPath, data);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
deleteFile(relativePath: string) {
|
|
41
|
+
const absPath = this.resolveFile(relativePath);
|
|
42
|
+
deleteFileBuffered(absPath);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
checkFile(relativePath: string): boolean {
|
|
46
|
+
const absPath = this.resolveFile(relativePath);
|
|
47
|
+
try {
|
|
48
|
+
const stats = fs.statSync(absPath);
|
|
49
|
+
return !!stats ? true : false;
|
|
50
|
+
} catch (e) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
getComponentFileContents(
|
|
56
|
+
projectId: string,
|
|
57
|
+
componentId: string
|
|
58
|
+
): string | undefined {
|
|
59
|
+
const plasmicJson: PlasmicConfig = JSON.parse(
|
|
60
|
+
this.readFile(CONFIG_FILE_NAME)
|
|
61
|
+
);
|
|
62
|
+
const srcDir = plasmicJson.srcDir;
|
|
63
|
+
const projectConfig = plasmicJson.projects.find(
|
|
64
|
+
(p) => p.projectId === projectId
|
|
65
|
+
);
|
|
66
|
+
if (!projectConfig) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const componentConfig = projectConfig.components.find(
|
|
70
|
+
(c) => c.id === componentId
|
|
71
|
+
);
|
|
72
|
+
if (!componentConfig) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const data = this.readFile(
|
|
76
|
+
path.join(srcDir, componentConfig.renderModuleFilePath)
|
|
77
|
+
);
|
|
78
|
+
return data;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
plasmicAuthPath(): string {
|
|
82
|
+
return this.resolveFile(AUTH_FILE_NAME);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
writePlasmicAuth(json: AuthConfig) {
|
|
86
|
+
this.writeFile(AUTH_FILE_NAME, JSON.stringify(json));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
deletePlasmicAuth() {
|
|
90
|
+
this.deleteFile(AUTH_FILE_NAME);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
plasmicJsonPath(): string {
|
|
94
|
+
return this.resolveFile(CONFIG_FILE_NAME);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
readPlasmicJson(): PlasmicConfig {
|
|
98
|
+
return JSON.parse(this.readFile(CONFIG_FILE_NAME));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
writePlasmicJson(json: PlasmicConfig) {
|
|
102
|
+
this.writeFile(CONFIG_FILE_NAME, JSON.stringify(json));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
deletePlasmicJson() {
|
|
106
|
+
this.deleteFile(CONFIG_FILE_NAME);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
plasmicLoaderJsonPath() {
|
|
110
|
+
return this.resolveFile(LOADER_CONFIG_FILE_NAME);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
readPlasmicLoaderJson(): PlasmicLoaderConfig {
|
|
114
|
+
return JSON.parse(this.readFile(LOADER_CONFIG_FILE_NAME));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import inquirer from "inquirer";
|
|
2
|
+
import { logger } from "../deps";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Provide a standardized way to ask user to continue
|
|
6
|
+
* @param message
|
|
7
|
+
* @param yes - If true, always return true without prompting.
|
|
8
|
+
* @param default - Override the default value returned if the user presses enter
|
|
9
|
+
*/
|
|
10
|
+
export async function confirmWithUser(
|
|
11
|
+
message: string,
|
|
12
|
+
yes?: boolean,
|
|
13
|
+
defaultAnswer?: "y" | "n"
|
|
14
|
+
): Promise<boolean> {
|
|
15
|
+
if (process.env.QUIET) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!!yes) {
|
|
20
|
+
if (!process.env.QUIET) {
|
|
21
|
+
logger.info(`${message} (Y/n): y`);
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
defaultAnswer = defaultAnswer ?? "y";
|
|
27
|
+
const isDefaultYes = defaultAnswer === "y";
|
|
28
|
+
const choices = `(${isDefaultYes ? "Y" : "y"}/${isDefaultYes ? "n" : "N"})`;
|
|
29
|
+
const res = await inquirer.prompt([
|
|
30
|
+
{
|
|
31
|
+
name: "continue",
|
|
32
|
+
message: `${message} ${choices}`,
|
|
33
|
+
default: defaultAnswer,
|
|
34
|
+
},
|
|
35
|
+
]);
|
|
36
|
+
return ["y", "yes"].includes(res.continue.toLowerCase());
|
|
37
|
+
}
|