@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,656 @@
|
|
|
1
|
+
import * as babel from "@babel/core";
|
|
2
|
+
import generate from "@babel/generator";
|
|
3
|
+
import * as parser from "@babel/parser";
|
|
4
|
+
import traverse, { Node } from "@babel/traverse";
|
|
5
|
+
import { ImportDeclaration } from "@babel/types";
|
|
6
|
+
import L from "lodash";
|
|
7
|
+
import * as Prettier from "prettier";
|
|
8
|
+
import { Options, resolveConfig } from "prettier";
|
|
9
|
+
import * as ts from "typescript";
|
|
10
|
+
import path from "upath";
|
|
11
|
+
import {
|
|
12
|
+
fixComponentCssReferences,
|
|
13
|
+
fixComponentImagesReferences,
|
|
14
|
+
} from "../actions/sync-images";
|
|
15
|
+
import { logger } from "../deps";
|
|
16
|
+
import { HandledError } from "../utils/error";
|
|
17
|
+
import {
|
|
18
|
+
CodeComponentConfig,
|
|
19
|
+
ComponentConfig,
|
|
20
|
+
GlobalVariantGroupConfig,
|
|
21
|
+
IconConfig,
|
|
22
|
+
ImageConfig,
|
|
23
|
+
PlasmicConfig,
|
|
24
|
+
PlasmicContext,
|
|
25
|
+
ProjectConfig,
|
|
26
|
+
} from "./config-utils";
|
|
27
|
+
import {
|
|
28
|
+
existsBuffered,
|
|
29
|
+
makeFilePath,
|
|
30
|
+
readFileText,
|
|
31
|
+
stripExtension,
|
|
32
|
+
writeFileContent,
|
|
33
|
+
} from "./file-utils";
|
|
34
|
+
import { assert, flatMap } from "./lang-utils";
|
|
35
|
+
|
|
36
|
+
export const formatAsLocal = (
|
|
37
|
+
content: string,
|
|
38
|
+
filePath: string,
|
|
39
|
+
baseDir: string,
|
|
40
|
+
defaultOpts: Options = {}
|
|
41
|
+
) => {
|
|
42
|
+
const opts = resolveConfig.sync(baseDir) || defaultOpts;
|
|
43
|
+
opts.filepath = filePath;
|
|
44
|
+
|
|
45
|
+
// Running Prettier multiple times may actually yield different results!
|
|
46
|
+
// Here we run it twice, just to be safe... :-/
|
|
47
|
+
const res = Prettier.format(content, opts);
|
|
48
|
+
const res2 = Prettier.format(res, opts);
|
|
49
|
+
return res2;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const nodeToFormattedCode = (
|
|
53
|
+
n: Node,
|
|
54
|
+
baseDir: string,
|
|
55
|
+
unformatted?: boolean,
|
|
56
|
+
commentsToRemove?: Set<string>
|
|
57
|
+
) => {
|
|
58
|
+
const c = generate(n, {
|
|
59
|
+
retainLines: true,
|
|
60
|
+
shouldPrintComment: (c) => !commentsToRemove || !commentsToRemove.has(c),
|
|
61
|
+
}).code;
|
|
62
|
+
return unformatted
|
|
63
|
+
? c
|
|
64
|
+
: formatAsLocal(c, "/tmp/x.tsx", baseDir, {
|
|
65
|
+
trailingComma: "none",
|
|
66
|
+
arrowParens: "avoid",
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
function findImportSpecifierWithAlias(
|
|
71
|
+
importDecl: ImportDeclaration,
|
|
72
|
+
local: string
|
|
73
|
+
) {
|
|
74
|
+
for (const spec of importDecl.specifiers) {
|
|
75
|
+
if (spec.type === "ImportSpecifier" && spec.local.name === local) {
|
|
76
|
+
return spec;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function findImportDefaultSpecifier(importDecl: ImportDeclaration) {
|
|
83
|
+
for (const spec of importDecl.specifiers) {
|
|
84
|
+
if (spec.type === "ImportDefaultSpecifier") {
|
|
85
|
+
return spec;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function ensureImportSpecifierWithAlias(
|
|
92
|
+
decl: ImportDeclaration,
|
|
93
|
+
imported: string,
|
|
94
|
+
alias: string
|
|
95
|
+
) {
|
|
96
|
+
const existing = findImportSpecifierWithAlias(decl, alias);
|
|
97
|
+
if (existing) {
|
|
98
|
+
if (existing.imported.type === "Identifier") {
|
|
99
|
+
existing.imported.name = imported;
|
|
100
|
+
} else {
|
|
101
|
+
existing.imported.value = imported;
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
decl.specifiers = decl.specifiers.filter((specifier) => {
|
|
105
|
+
if (
|
|
106
|
+
specifier.type === "ImportDefaultSpecifier" &&
|
|
107
|
+
specifier.local.name === alias
|
|
108
|
+
) {
|
|
109
|
+
// If we are importing a default for a name that will collide with our
|
|
110
|
+
// desired alias, then the default import is wrong and we skip it.
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
return true;
|
|
114
|
+
});
|
|
115
|
+
decl.specifiers.push(
|
|
116
|
+
babel.types.importSpecifier(
|
|
117
|
+
babel.types.identifier(alias),
|
|
118
|
+
babel.types.identifier(imported)
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function ensureImportDefaultSpecifier(
|
|
125
|
+
decl: ImportDeclaration,
|
|
126
|
+
defaultExport: string
|
|
127
|
+
) {
|
|
128
|
+
const existing = findImportDefaultSpecifier(decl);
|
|
129
|
+
if (existing) {
|
|
130
|
+
existing.local.name = defaultExport;
|
|
131
|
+
} else {
|
|
132
|
+
decl.specifiers.splice(
|
|
133
|
+
0,
|
|
134
|
+
0,
|
|
135
|
+
babel.types.importDefaultSpecifier(babel.types.identifier(defaultExport))
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
interface PlasmicImportSpec {
|
|
141
|
+
id: string;
|
|
142
|
+
type: PlasmicImportType;
|
|
143
|
+
node: ImportDeclaration;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
type PlasmicImportType =
|
|
147
|
+
| "render"
|
|
148
|
+
| "css"
|
|
149
|
+
| "component"
|
|
150
|
+
| "globalVariant"
|
|
151
|
+
| "projectcss"
|
|
152
|
+
| "defaultcss"
|
|
153
|
+
| "icon"
|
|
154
|
+
| "picture"
|
|
155
|
+
| "jsBundle"
|
|
156
|
+
| "codeComponent"
|
|
157
|
+
| undefined;
|
|
158
|
+
|
|
159
|
+
function tryParsePlasmicImportSpec(node: ImportDeclaration) {
|
|
160
|
+
const c = node.trailingComments?.[0];
|
|
161
|
+
if (!c) {
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
const m = c.value.match(
|
|
165
|
+
/plasmic-import:\s+([\w-]+)(?:\/(component|css|render|globalVariant|projectcss|defaultcss|icon|picture|jsBundle|codeComponent))?/
|
|
166
|
+
);
|
|
167
|
+
if (m) {
|
|
168
|
+
return { id: m[1], type: m[2] as PlasmicImportType } as PlasmicImportSpec;
|
|
169
|
+
}
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function filterUnformattedMarker(code: string, changed: boolean) {
|
|
174
|
+
const lines = code.split("\n");
|
|
175
|
+
const isUnformattedMarker = (line: string) =>
|
|
176
|
+
line.trim() === "// plasmic-unformatted";
|
|
177
|
+
changed = changed || lines.some(isUnformattedMarker);
|
|
178
|
+
code = lines.filter((line) => !isUnformattedMarker(line)).join("\n");
|
|
179
|
+
return [code, changed] as const;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Given the argument `code` string, for module at `fromPath`, replaces all Plasmic imports
|
|
184
|
+
* for modules found in `compConfigsMap`.
|
|
185
|
+
*/
|
|
186
|
+
export function replaceImports(
|
|
187
|
+
context: PlasmicContext,
|
|
188
|
+
code: string,
|
|
189
|
+
fromPath: string,
|
|
190
|
+
fixImportContext: FixImportContext,
|
|
191
|
+
removeImportDirective: boolean,
|
|
192
|
+
baseDir: string,
|
|
193
|
+
changed = false
|
|
194
|
+
) {
|
|
195
|
+
[code, changed] = filterUnformattedMarker(code, changed);
|
|
196
|
+
const file = parser.parse(code, {
|
|
197
|
+
strictMode: true,
|
|
198
|
+
sourceType: "module",
|
|
199
|
+
plugins: [
|
|
200
|
+
// At a minimum, we need to parse jsx and typescript
|
|
201
|
+
"jsx",
|
|
202
|
+
"typescript",
|
|
203
|
+
|
|
204
|
+
// There are also various features that people may have... May just
|
|
205
|
+
// need to add as we encounter them...
|
|
206
|
+
"classProperties",
|
|
207
|
+
],
|
|
208
|
+
});
|
|
209
|
+
const commentsToRemove = new Set<string>();
|
|
210
|
+
file.program.body.forEach((stmt) => {
|
|
211
|
+
if (stmt.type !== "ImportDeclaration") {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const importStmt: ImportDeclaration = stmt;
|
|
215
|
+
const spec = tryParsePlasmicImportSpec(importStmt);
|
|
216
|
+
if (!spec) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
changed = true;
|
|
220
|
+
if (removeImportDirective) {
|
|
221
|
+
commentsToRemove.add(stmt.trailingComments?.[0].value || "");
|
|
222
|
+
}
|
|
223
|
+
const type = spec.type;
|
|
224
|
+
const uuid = spec.id;
|
|
225
|
+
if (type === "component") {
|
|
226
|
+
// instantiation of a mapped or managed component
|
|
227
|
+
const compConfig = fixImportContext.components[uuid];
|
|
228
|
+
if (!compConfig) {
|
|
229
|
+
throwMissingReference(context, "component", uuid, fromPath);
|
|
230
|
+
}
|
|
231
|
+
const { modulePath, exportName } = compConfig.importSpec;
|
|
232
|
+
if (exportName) {
|
|
233
|
+
// ensure import { ${exportName} as ${compConfig.name} }
|
|
234
|
+
ensureImportSpecifierWithAlias(stmt, exportName, compConfig.name);
|
|
235
|
+
} else {
|
|
236
|
+
// Keep the same name as it might be different from compConfig.name due
|
|
237
|
+
// to name collisions.
|
|
238
|
+
// ensureImportDefaultSpecifier(stmt, compConfig.name);
|
|
239
|
+
}
|
|
240
|
+
const realPath = makeImportPath(context, fromPath, modulePath, true);
|
|
241
|
+
stmt.source.value = realPath;
|
|
242
|
+
} else if (type === "render") {
|
|
243
|
+
// import of the PP blackbox
|
|
244
|
+
const compConfig = fixImportContext.components[uuid];
|
|
245
|
+
if (!compConfig) {
|
|
246
|
+
throwMissingReference(context, "component", uuid, fromPath);
|
|
247
|
+
}
|
|
248
|
+
const realPath = makeImportPath(
|
|
249
|
+
context,
|
|
250
|
+
fromPath,
|
|
251
|
+
compConfig.renderModuleFilePath,
|
|
252
|
+
true
|
|
253
|
+
);
|
|
254
|
+
stmt.source.value = realPath;
|
|
255
|
+
} else if (type === "css") {
|
|
256
|
+
// import of the PP css file
|
|
257
|
+
const compConfig = fixImportContext.components[uuid];
|
|
258
|
+
if (!compConfig) {
|
|
259
|
+
throwMissingReference(context, "component", uuid, fromPath);
|
|
260
|
+
}
|
|
261
|
+
const realPath = makeImportPath(
|
|
262
|
+
context,
|
|
263
|
+
fromPath,
|
|
264
|
+
compConfig.cssFilePath,
|
|
265
|
+
false
|
|
266
|
+
);
|
|
267
|
+
stmt.source.value = realPath;
|
|
268
|
+
} else if (type === "globalVariant") {
|
|
269
|
+
// import of global context
|
|
270
|
+
const variantConfig = fixImportContext.globalVariants[uuid];
|
|
271
|
+
if (!variantConfig) {
|
|
272
|
+
throwMissingReference(context, "global variant", uuid, fromPath);
|
|
273
|
+
}
|
|
274
|
+
const realPath = makeImportPath(
|
|
275
|
+
context,
|
|
276
|
+
fromPath,
|
|
277
|
+
variantConfig.contextFilePath,
|
|
278
|
+
true
|
|
279
|
+
);
|
|
280
|
+
stmt.source.value = realPath;
|
|
281
|
+
} else if (type === "icon") {
|
|
282
|
+
// import of global context
|
|
283
|
+
const iconConfig = fixImportContext.icons[uuid];
|
|
284
|
+
if (!iconConfig) {
|
|
285
|
+
throwMissingReference(context, "icon", uuid, fromPath);
|
|
286
|
+
}
|
|
287
|
+
const realPath = makeImportPath(
|
|
288
|
+
context,
|
|
289
|
+
fromPath,
|
|
290
|
+
iconConfig.moduleFilePath,
|
|
291
|
+
true
|
|
292
|
+
);
|
|
293
|
+
stmt.source.value = realPath;
|
|
294
|
+
} else if (type === "picture") {
|
|
295
|
+
const imageConfig = fixImportContext.images[uuid];
|
|
296
|
+
if (!imageConfig) {
|
|
297
|
+
throwMissingReference(context, "image", uuid, fromPath);
|
|
298
|
+
}
|
|
299
|
+
const realPath = makeImportPath(
|
|
300
|
+
context,
|
|
301
|
+
fromPath,
|
|
302
|
+
imageConfig.filePath,
|
|
303
|
+
false
|
|
304
|
+
);
|
|
305
|
+
stmt.source.value = realPath;
|
|
306
|
+
} else if (type === "projectcss") {
|
|
307
|
+
const projectConfig = fixImportContext.projects[uuid];
|
|
308
|
+
if (!projectConfig) {
|
|
309
|
+
throwMissingReference(context, "project", uuid, fromPath);
|
|
310
|
+
}
|
|
311
|
+
const realPath = makeImportPath(
|
|
312
|
+
context,
|
|
313
|
+
fromPath,
|
|
314
|
+
projectConfig.cssFilePath,
|
|
315
|
+
false
|
|
316
|
+
);
|
|
317
|
+
stmt.source.value = realPath;
|
|
318
|
+
} else if (type === "defaultcss") {
|
|
319
|
+
const realPath = makeImportPath(
|
|
320
|
+
context,
|
|
321
|
+
fromPath,
|
|
322
|
+
fixImportContext.config.style.defaultStyleCssFilePath,
|
|
323
|
+
false
|
|
324
|
+
);
|
|
325
|
+
stmt.source.value = realPath;
|
|
326
|
+
} else if (type === "codeComponent") {
|
|
327
|
+
const meta = fixImportContext.codeComponentMetas[uuid];
|
|
328
|
+
if (meta.componentImportPath[0] === ".") {
|
|
329
|
+
// Relative path from the project root
|
|
330
|
+
const toPath = path.join(process.cwd(), meta.componentImportPath);
|
|
331
|
+
assert(path.isAbsolute(toPath));
|
|
332
|
+
const realPath = makeImportPath(context, fromPath, toPath, true, true);
|
|
333
|
+
stmt.source.value = realPath;
|
|
334
|
+
} else {
|
|
335
|
+
// npm package
|
|
336
|
+
stmt.source.value = meta.componentImportPath;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
if (!changed) {
|
|
342
|
+
return code;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return nodeToFormattedCode(file, baseDir, !changed, commentsToRemove);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function throwMissingReference(
|
|
349
|
+
context: PlasmicContext,
|
|
350
|
+
itemType: string,
|
|
351
|
+
uuid: string,
|
|
352
|
+
fromPath: string
|
|
353
|
+
) {
|
|
354
|
+
let recFix = `Please make sure projects that "${uuid}" depends on have already been synced.`;
|
|
355
|
+
if (context.cliArgs.nonRecursive) {
|
|
356
|
+
recFix = `Please run "plasmic sync" without the --non-recursive flag to sync dependencies.`;
|
|
357
|
+
} else if (!context.cliArgs.force) {
|
|
358
|
+
recFix = `Please run "plasmic sync" with --force flag to force-sync the dependencies.`;
|
|
359
|
+
}
|
|
360
|
+
throw new HandledError(
|
|
361
|
+
`Encountered Plasmic ${itemType} "${uuid}" in ${fromPath} that are being used but have not been synced. ${recFix}`
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function makeImportPath(
|
|
366
|
+
context: PlasmicContext,
|
|
367
|
+
fromPath: string,
|
|
368
|
+
toPath: string,
|
|
369
|
+
stripExt: boolean,
|
|
370
|
+
forceRelative = false
|
|
371
|
+
) {
|
|
372
|
+
let result = toPath;
|
|
373
|
+
if (forceRelative || isLocalModulePath(toPath)) {
|
|
374
|
+
result = path.relative(
|
|
375
|
+
makeFilePath(context, path.dirname(fromPath)),
|
|
376
|
+
makeFilePath(context, toPath)
|
|
377
|
+
);
|
|
378
|
+
if (!result.startsWith(".")) {
|
|
379
|
+
result = `./${result}`;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
if (stripExt) {
|
|
383
|
+
result = stripExtension(result);
|
|
384
|
+
}
|
|
385
|
+
return result;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Returns true if the argument ComponentConfig.importSpec.modulePath is referencing
|
|
390
|
+
* a local file
|
|
391
|
+
*/
|
|
392
|
+
export function isLocalModulePath(modulePath: string) {
|
|
393
|
+
// It is a local file reference if the importPath includes the file extension
|
|
394
|
+
return !!path.extname(modulePath);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export interface ComponentUpdateSummary {
|
|
398
|
+
// Whether the skeleton module was modified or created.
|
|
399
|
+
skeletonModuleModified: boolean;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export interface FixImportContext {
|
|
403
|
+
config: PlasmicConfig;
|
|
404
|
+
components: Record<string, ComponentConfig>;
|
|
405
|
+
codeComponentMetas: Record<string, CodeComponentConfig>;
|
|
406
|
+
globalVariants: Record<string, GlobalVariantGroupConfig>;
|
|
407
|
+
icons: Record<string, IconConfig>;
|
|
408
|
+
images: Record<string, ImageConfig>;
|
|
409
|
+
projects: Record<string, ProjectConfig>;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export const mkFixImportContext = (config: PlasmicConfig) => {
|
|
413
|
+
const allComponents = flatMap(config.projects, (p) => p.components);
|
|
414
|
+
const components = L.keyBy(allComponents, (c) => c.id);
|
|
415
|
+
const allCodeComponents = flatMap(
|
|
416
|
+
config.projects,
|
|
417
|
+
(p) => p.codeComponents || []
|
|
418
|
+
);
|
|
419
|
+
const codeComponentMetas = L.keyBy(allCodeComponents, (c) => c.id);
|
|
420
|
+
const globalVariants = L.keyBy(
|
|
421
|
+
config.globalVariants.variantGroups,
|
|
422
|
+
(c) => c.id
|
|
423
|
+
);
|
|
424
|
+
const icons = L.keyBy(
|
|
425
|
+
flatMap(config.projects, (p) => p.icons),
|
|
426
|
+
(c) => c.id
|
|
427
|
+
);
|
|
428
|
+
const images = L.keyBy(
|
|
429
|
+
flatMap(config.projects, (p) => p.images),
|
|
430
|
+
(c) => c.id
|
|
431
|
+
);
|
|
432
|
+
const projects = L.keyBy(config.projects, (p) => p.projectId);
|
|
433
|
+
return {
|
|
434
|
+
config,
|
|
435
|
+
components,
|
|
436
|
+
codeComponentMetas,
|
|
437
|
+
globalVariants,
|
|
438
|
+
icons,
|
|
439
|
+
images,
|
|
440
|
+
projects,
|
|
441
|
+
};
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Assuming that all the files referenced in PlasmicConfig are correct, fixes import statements using PlasmicConfig
|
|
446
|
+
* file locations as the source of truth.
|
|
447
|
+
*/
|
|
448
|
+
export async function fixAllImportStatements(
|
|
449
|
+
context: PlasmicContext,
|
|
450
|
+
baseDir: string,
|
|
451
|
+
summary?: Map<string, ComponentUpdateSummary>
|
|
452
|
+
) {
|
|
453
|
+
logger.info("Fixing import statements...");
|
|
454
|
+
const config = context.config;
|
|
455
|
+
const fixImportContext = mkFixImportContext(config);
|
|
456
|
+
for (const project of config.projects) {
|
|
457
|
+
for (const compConfig of project.components) {
|
|
458
|
+
const compSummary = summary?.get(compConfig.id);
|
|
459
|
+
if (summary && !compSummary) {
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
const fixSkeletonModule = compSummary
|
|
463
|
+
? compSummary.skeletonModuleModified
|
|
464
|
+
: true;
|
|
465
|
+
if (!summary || compSummary) {
|
|
466
|
+
await fixComponentImportStatements(
|
|
467
|
+
context,
|
|
468
|
+
compConfig,
|
|
469
|
+
fixImportContext,
|
|
470
|
+
fixSkeletonModule,
|
|
471
|
+
baseDir
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
async function fixComponentImportStatements(
|
|
479
|
+
context: PlasmicContext,
|
|
480
|
+
compConfig: ComponentConfig,
|
|
481
|
+
fixImportContext: FixImportContext,
|
|
482
|
+
fixSkeletonModule: boolean,
|
|
483
|
+
baseDir: string
|
|
484
|
+
) {
|
|
485
|
+
// If ComponentConfig.importPath is still a local file, we best-effort also fix up the import statements there.
|
|
486
|
+
if (
|
|
487
|
+
isLocalModulePath(compConfig.importSpec.modulePath) &&
|
|
488
|
+
fixSkeletonModule
|
|
489
|
+
) {
|
|
490
|
+
await fixFileImportStatements(
|
|
491
|
+
context,
|
|
492
|
+
compConfig.importSpec.modulePath,
|
|
493
|
+
fixImportContext,
|
|
494
|
+
true,
|
|
495
|
+
baseDir
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
let renderModuleChanged = false;
|
|
500
|
+
|
|
501
|
+
if (["files", "public-files"].includes(context.config.images.scheme)) {
|
|
502
|
+
await fixComponentCssReferences(
|
|
503
|
+
context,
|
|
504
|
+
fixImportContext,
|
|
505
|
+
compConfig.cssFilePath
|
|
506
|
+
);
|
|
507
|
+
if (context.config.images.scheme === "public-files") {
|
|
508
|
+
renderModuleChanged = await fixComponentImagesReferences(
|
|
509
|
+
context,
|
|
510
|
+
fixImportContext,
|
|
511
|
+
compConfig.renderModuleFilePath
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Fix file imports and run prettier just after fixing image references
|
|
517
|
+
await fixFileImportStatements(
|
|
518
|
+
context,
|
|
519
|
+
compConfig.renderModuleFilePath,
|
|
520
|
+
fixImportContext,
|
|
521
|
+
false,
|
|
522
|
+
baseDir,
|
|
523
|
+
renderModuleChanged
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
async function fixFileImportStatements(
|
|
528
|
+
context: PlasmicContext,
|
|
529
|
+
srcDirFilePath: string,
|
|
530
|
+
fixImportContext: FixImportContext,
|
|
531
|
+
removeImportDirective: boolean,
|
|
532
|
+
baseDir: string,
|
|
533
|
+
fileHasChanged = false
|
|
534
|
+
) {
|
|
535
|
+
const filePath = makeFilePath(context, srcDirFilePath);
|
|
536
|
+
if (!existsBuffered(filePath)) {
|
|
537
|
+
logger.warn(
|
|
538
|
+
`Cannot fix imports in non-existing file ${srcDirFilePath}. Check your plasmic.json file for invalid entries.`
|
|
539
|
+
);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const prevContent = readFileText(filePath).toString();
|
|
544
|
+
|
|
545
|
+
const newContent = replaceImports(
|
|
546
|
+
context,
|
|
547
|
+
prevContent,
|
|
548
|
+
srcDirFilePath,
|
|
549
|
+
fixImportContext,
|
|
550
|
+
removeImportDirective,
|
|
551
|
+
baseDir,
|
|
552
|
+
fileHasChanged
|
|
553
|
+
);
|
|
554
|
+
if (prevContent !== newContent) {
|
|
555
|
+
await writeFileContent(context, srcDirFilePath, newContent, {
|
|
556
|
+
force: true,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
class CompilerOptions {
|
|
562
|
+
private static opts: ts.CompilerOptions | undefined = undefined;
|
|
563
|
+
|
|
564
|
+
static getOpts() {
|
|
565
|
+
if (!this.opts) {
|
|
566
|
+
let curDir = __dirname;
|
|
567
|
+
let configPath = "";
|
|
568
|
+
do {
|
|
569
|
+
curDir = path.join(curDir, "..");
|
|
570
|
+
configPath = path.join(curDir, "tsconfig-transform.json");
|
|
571
|
+
} while (!existsBuffered(configPath));
|
|
572
|
+
const c = ts.readConfigFile(configPath, (path) => readFileText(path));
|
|
573
|
+
this.opts = ts.convertCompilerOptionsFromJson(
|
|
574
|
+
c.config.compilerOptions,
|
|
575
|
+
"."
|
|
576
|
+
).options;
|
|
577
|
+
}
|
|
578
|
+
return this.opts;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
export const tsxToJsx = (code: string) => {
|
|
583
|
+
// when the code has jsx pragma, typescript will remove comments, and remove
|
|
584
|
+
// "import React from 'React'" if React is unused. So we first invalidate it.
|
|
585
|
+
// We also need to add the usageMagic to prevent typescript from remove the
|
|
586
|
+
// import of ncreatePlasmicElementProxy.
|
|
587
|
+
const usageMagic = "\ncreatePlasmicElementProxy();";
|
|
588
|
+
const jsxPragmas = ["jsx", "jsxFrag", "jsxRuntime", "jsxImportSource"];
|
|
589
|
+
|
|
590
|
+
function prepForTranspile(str: string) {
|
|
591
|
+
for (const p of jsxPragmas) {
|
|
592
|
+
str = str.replace(`/** @${p} `, `/** @ ${p} `);
|
|
593
|
+
}
|
|
594
|
+
return str + usageMagic;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function fixPostTranspile(str: string) {
|
|
598
|
+
for (const p of jsxPragmas) {
|
|
599
|
+
str = str.replace(`/** @ ${p} `, `/** @${p} `);
|
|
600
|
+
}
|
|
601
|
+
str = str.replace(usageMagic, "");
|
|
602
|
+
return str;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
let result = ts.transpileModule(prepForTranspile(code), {
|
|
606
|
+
compilerOptions: CompilerOptions.getOpts(),
|
|
607
|
+
});
|
|
608
|
+
return fixPostTranspile(result.outputText);
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
export function maybeConvertTsxToJsx(
|
|
612
|
+
fileName: string,
|
|
613
|
+
content: string,
|
|
614
|
+
baseDir: string
|
|
615
|
+
) {
|
|
616
|
+
if (fileName.endsWith("tsx")) {
|
|
617
|
+
const jsFileName = stripExtension(fileName) + ".jsx";
|
|
618
|
+
const jsContent = formatScript(tsxToJsx(content), baseDir);
|
|
619
|
+
return [jsFileName, jsContent];
|
|
620
|
+
}
|
|
621
|
+
return [fileName, content];
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
export const formatScript = (code: string, baseDir: string) => {
|
|
625
|
+
const file = parser.parse(code, {
|
|
626
|
+
strictMode: true,
|
|
627
|
+
sourceType: "module",
|
|
628
|
+
plugins: ["jsx", "typescript"],
|
|
629
|
+
});
|
|
630
|
+
let newLineMarker = "THIS_SHALL_BE_NEW_LINE";
|
|
631
|
+
while (code.includes(newLineMarker)) {
|
|
632
|
+
newLineMarker = newLineMarker + "_REALLY";
|
|
633
|
+
}
|
|
634
|
+
traverse(file, {
|
|
635
|
+
Statement: function (path) {
|
|
636
|
+
if (
|
|
637
|
+
file.program.body.includes(path.node) &&
|
|
638
|
+
path.node.type !== "ImportDeclaration"
|
|
639
|
+
) {
|
|
640
|
+
path.insertBefore(babel.types.stringLiteral(newLineMarker));
|
|
641
|
+
path.skip();
|
|
642
|
+
}
|
|
643
|
+
},
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
const withmarkers = nodeToFormattedCode(file, baseDir, true);
|
|
647
|
+
const withNewLines = withmarkers.replace(
|
|
648
|
+
new RegExp(`"${newLineMarker}"`, "g"),
|
|
649
|
+
"\n"
|
|
650
|
+
);
|
|
651
|
+
return formatAsLocal(withNewLines, "/tmp/x.tsx", baseDir, {
|
|
652
|
+
printWidth: 80,
|
|
653
|
+
tabWidth: 2,
|
|
654
|
+
useTabs: false,
|
|
655
|
+
});
|
|
656
|
+
};
|