@plasmicapp/cli 0.1.176 → 0.1.179

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.
@@ -0,0 +1,8 @@
1
+ export interface LocalizationStringsArgs {
2
+ host: string;
3
+ projects: readonly string[];
4
+ format: "po" | "json" | "lingui";
5
+ output: string;
6
+ forceOverwrite: boolean;
7
+ }
8
+ export declare function localizationStrings(opts: LocalizationStringsArgs): Promise<void>;
@@ -0,0 +1,56 @@
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.localizationStrings = void 0;
16
+ const chalk_1 = __importDefault(require("chalk"));
17
+ const api_1 = require("../api");
18
+ const deps_1 = require("../deps");
19
+ const lib_1 = require("../lib");
20
+ const auth_utils_1 = require("../utils/auth-utils");
21
+ const config_utils_1 = require("../utils/config-utils");
22
+ const file_utils_1 = require("../utils/file-utils");
23
+ const user_utils_1 = require("../utils/user-utils");
24
+ function localizationStrings(opts) {
25
+ return __awaiter(this, void 0, void 0, function* () {
26
+ if (!opts.projects || opts.projects.length === 0) {
27
+ throw new lib_1.HandledError(`Missing projects.`);
28
+ }
29
+ const output = !opts.output
30
+ ? opts.format === "po"
31
+ ? "data.po"
32
+ : "data.json"
33
+ : opts.output;
34
+ const auth = yield auth_utils_1.getOrStartAuth({
35
+ baseDir: "",
36
+ host: opts.host || config_utils_1.DEFAULT_HOST,
37
+ });
38
+ if (auth) {
39
+ const api = new api_1.PlasmicApi(auth);
40
+ deps_1.logger.info(`Generating localization strings for ${chalk_1.default.bold(opts.projects.join(", "))}...`);
41
+ const data = yield api.genLocalizationStrings(opts.projects, opts.format);
42
+ if (file_utils_1.existsBuffered(output)) {
43
+ const overwrite = yield user_utils_1.confirmWithUser(`File ${output} already exists. Do you want to overwrite?`, opts.forceOverwrite);
44
+ if (!overwrite) {
45
+ throw new lib_1.HandledError(`Cannot write to ${output}; file already exists.`);
46
+ }
47
+ }
48
+ file_utils_1.writeFileText(output, data);
49
+ deps_1.logger.info(`Localization strings have been written to ${output}`);
50
+ }
51
+ else {
52
+ deps_1.logger.error("Missing auth information");
53
+ }
54
+ });
55
+ }
56
+ exports.localizationStrings = localizationStrings;
@@ -13,9 +13,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.getGlobalContextsResourcePath = exports.syncGlobalContexts = void 0;
16
+ const lodash_1 = __importDefault(require("lodash"));
16
17
  const deps_1 = require("../deps");
17
18
  const code_utils_1 = require("../utils/code-utils");
18
- const lodash_1 = __importDefault(require("lodash"));
19
19
  const file_utils_1 = require("../utils/file-utils");
20
20
  const COMPONENT_NAME = "PlasmicGlobalContextsProvider";
21
21
  function syncGlobalContexts(context, projectMeta, projectConfig, projectLock, checksums, baseDir) {
@@ -28,7 +28,7 @@ function syncGlobalContexts(context, projectMeta, projectConfig, projectLock, ch
28
28
  if (context.config.code.lang === "js") {
29
29
  projectMeta.globalContextBundle.contextModule = code_utils_1.formatScript(code_utils_1.tsxToJsx(projectMeta.globalContextBundle.contextModule), baseDir);
30
30
  }
31
- file_utils_1.writeFileContent(context, resourcePath, projectMeta.globalContextBundle.contextModule, { force: false });
31
+ file_utils_1.writeFileContent(context, resourcePath, projectMeta.globalContextBundle.contextModule, { force: true });
32
32
  projectConfig.globalContextsFilePath = resourcePath;
33
33
  const fl = projectLock.fileLocks.find((fl) => fl.assetId === projectConfig.projectId && fl.type === "globalContexts");
34
34
  if (fl) {
@@ -74,17 +74,47 @@ function watchProjects(opts, metadataDefaults, onProjectUpdate) {
74
74
  socket.on("error", (data) => {
75
75
  reject(new error_1.HandledError(data));
76
76
  });
77
- socket.on("update", (data) => __awaiter(this, void 0, void 0, function* () {
77
+ socket.on("update", asyncOneAtATime((data) => __awaiter(this, void 0, void 0, function* () {
78
78
  // Just run syncProjects() for now when any project has been updated
79
79
  // Note on the 'updated to revision' part: this is parsed by the
80
80
  // loader package to know that we finished updating the components.
81
81
  yield sync_1.sync(syncOpts, syncMetadata);
82
82
  deps_1.logger.info(`[${moment_1.default().format("HH:mm:ss")}] Project ${data.projectId} updated to revision ${data.revisionNum}`);
83
83
  onProjectUpdate === null || onProjectUpdate === void 0 ? void 0 : onProjectUpdate();
84
- }));
84
+ }), true));
85
85
  });
86
86
  deps_1.logger.info(`Watching projects ${latestProjects} ...`);
87
87
  return promise;
88
88
  });
89
89
  }
90
90
  exports.watchProjects = watchProjects;
91
+ function asyncOneAtATime(f, bounceValue) {
92
+ let waitingCall = undefined, currentPromise = undefined;
93
+ function invoke({ args, resolve, reject }) {
94
+ const onCompletion = () => {
95
+ currentPromise = undefined;
96
+ if (waitingCall) {
97
+ invoke(waitingCall);
98
+ waitingCall = undefined;
99
+ }
100
+ };
101
+ currentPromise = f(...args);
102
+ currentPromise.then(onCompletion, onCompletion);
103
+ currentPromise.then(resolve, reject);
104
+ }
105
+ return (...args) => {
106
+ return new Promise((resolve, reject) => {
107
+ if (!currentPromise) {
108
+ // Free to proceed.
109
+ invoke({ args, resolve, reject });
110
+ }
111
+ else {
112
+ // Evict current waiter, and enqueue self.
113
+ if (waitingCall) {
114
+ waitingCall.resolve(bounceValue);
115
+ }
116
+ waitingCall = { args, resolve, reject };
117
+ }
118
+ });
119
+ };
120
+ }
package/dist/api.d.ts CHANGED
@@ -188,12 +188,14 @@ export declare class PlasmicApi {
188
188
  metadata?: Metadata;
189
189
  }): Promise<ProjectBundle>;
190
190
  projectMeta(projectId: string): Promise<ProjectMetaInfo>;
191
+ genLocalizationStrings(projects: readonly string[], format: "po" | "json" | "lingui"): Promise<string>;
191
192
  uploadBundle(projectId: string, bundleName: string, bundleJs: string, css: string[], metaJson: string, genModulePath: string | undefined, genCssPaths: string[], pkgVersion: string | undefined, extraPropMetaJson: string | undefined, themeProviderWrapper: string | undefined, themeModule: string | undefined): Promise<StyleTokensMap>;
192
193
  projectStyleTokens(projectId: string, versionRange?: string): Promise<StyleTokensMap>;
193
194
  projectIcons(projectId: string, versionRange?: string, iconIds?: string[]): Promise<ProjectIconsResponse>;
194
195
  projectSyncMetadata(projectId: string, revision: number, rethrowAppError: boolean): Promise<ProjectSyncMetadataModel>;
195
196
  connectSocket(): SocketIOClient.Socket;
196
197
  private post;
198
+ private get;
197
199
  private makeErrorMessage;
198
200
  private makeHeaders;
199
201
  private projectIdsAndTokens?;
package/dist/api.js CHANGED
@@ -99,6 +99,14 @@ class PlasmicApi {
99
99
  return result.data;
100
100
  });
101
101
  }
102
+ genLocalizationStrings(projects, format) {
103
+ return __awaiter(this, void 0, void 0, function* () {
104
+ const result = yield this.get(`${this.codegenHost}/api/v1/localization/gen-texts?format=${format}&preview=true&${projects
105
+ .map((p) => `projectId=${p}`)
106
+ .join("&")}`);
107
+ return result.data;
108
+ });
109
+ }
102
110
  uploadBundle(projectId, bundleName, bundleJs, css, metaJson, genModulePath, genCssPaths, pkgVersion, extraPropMetaJson, themeProviderWrapper, themeModule) {
103
111
  return __awaiter(this, void 0, void 0, function* () {
104
112
  const result = yield this.post(`${this.codegenHost}/api/v1/projects/${projectId}/jsbundle/upload`, {
@@ -168,6 +176,26 @@ class PlasmicApi {
168
176
  }
169
177
  });
170
178
  }
179
+ get(url, rethrowAppError) {
180
+ return __awaiter(this, void 0, void 0, function* () {
181
+ try {
182
+ return yield axios_1.default.get(url, {
183
+ headers: this.makeHeaders(),
184
+ });
185
+ }
186
+ catch (e) {
187
+ const error = e;
188
+ const errorMsg = this.makeErrorMessage(error);
189
+ if (rethrowAppError) {
190
+ throw new AppServerError(errorMsg);
191
+ }
192
+ if (!errorMsg) {
193
+ throw e;
194
+ }
195
+ throw new error_1.HandledError(errorMsg);
196
+ }
197
+ });
198
+ }
171
199
  makeErrorMessage(error) {
172
200
  var _a, _b;
173
201
  const response = error.response;
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ const auth = __importStar(require("./actions/auth"));
30
30
  const fix_imports_1 = require("./actions/fix-imports");
31
31
  const info_1 = require("./actions/info");
32
32
  const init_1 = require("./actions/init");
33
+ const localization_strings_1 = require("./actions/localization-strings");
33
34
  const projectToken = __importStar(require("./actions/project-token"));
34
35
  const sync_1 = require("./actions/sync");
35
36
  const upload_bundle_1 = require("./actions/upload-bundle");
@@ -186,6 +187,33 @@ yargs_1.default
186
187
  type: "string",
187
188
  default: "https://studio.plasmic.app",
188
189
  }), (argv) => error_1.handleError(projectToken.projectToken(argv)))
190
+ .command("localization-strings", false, (yargs) => yargs
191
+ .option("projects", {
192
+ alias: "p",
193
+ describe: "One or more projects to generate localization strings, separated by comma. Version constraints can be specified using @. Example: projectid, projectid@>=version",
194
+ type: "array",
195
+ })
196
+ .option("host", {
197
+ describe: "Plasmic host to use",
198
+ type: "string",
199
+ default: "https://studio.plasmic.app",
200
+ })
201
+ .option("format", {
202
+ describe: 'Output format. Either "json", "po" or "lingui"',
203
+ type: "string",
204
+ choices: ["json", "po", "lingui"],
205
+ default: "json",
206
+ })
207
+ .option("output", {
208
+ alias: "o",
209
+ describe: "Output file",
210
+ type: "string",
211
+ })
212
+ .option("force-overwrite", {
213
+ type: "boolean",
214
+ describe: "Overwrite the output file.",
215
+ default: false,
216
+ }), (argv) => error_1.handleError(localization_strings_1.localizationStrings(argv)))
189
217
  .demandCommand()
190
218
  .strict()
191
219
  .help("h")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plasmicapp/cli",
3
- "version": "0.1.176",
3
+ "version": "0.1.179",
4
4
  "description": "plasmic cli for syncing local code with Plasmic designs",
5
5
  "engines": {
6
6
  "node": ">=12"
@@ -0,0 +1,57 @@
1
+ import chalk from "chalk";
2
+ import { PlasmicApi } from "../api";
3
+ import { logger } from "../deps";
4
+ import { HandledError } from "../lib";
5
+ import { getOrStartAuth } from "../utils/auth-utils";
6
+ import { DEFAULT_HOST } from "../utils/config-utils";
7
+ import { existsBuffered, writeFileText } from "../utils/file-utils";
8
+ import { confirmWithUser } from "../utils/user-utils";
9
+
10
+ export interface LocalizationStringsArgs {
11
+ host: string;
12
+ projects: readonly string[];
13
+ format: "po" | "json" | "lingui";
14
+ output: string;
15
+ forceOverwrite: boolean;
16
+ }
17
+
18
+ export async function localizationStrings(
19
+ opts: LocalizationStringsArgs
20
+ ): Promise<void> {
21
+ if (!opts.projects || opts.projects.length === 0) {
22
+ throw new HandledError(`Missing projects.`);
23
+ }
24
+ const output = !opts.output
25
+ ? opts.format === "po"
26
+ ? "data.po"
27
+ : "data.json"
28
+ : opts.output;
29
+ const auth = await getOrStartAuth({
30
+ baseDir: "",
31
+ host: opts.host || DEFAULT_HOST,
32
+ });
33
+ if (auth) {
34
+ const api = new PlasmicApi(auth);
35
+ logger.info(
36
+ `Generating localization strings for ${chalk.bold(
37
+ opts.projects.join(", ")
38
+ )}...`
39
+ );
40
+ const data = await api.genLocalizationStrings(opts.projects, opts.format);
41
+ if (existsBuffered(output)) {
42
+ const overwrite = await confirmWithUser(
43
+ `File ${output} already exists. Do you want to overwrite?`,
44
+ opts.forceOverwrite
45
+ );
46
+ if (!overwrite) {
47
+ throw new HandledError(
48
+ `Cannot write to ${output}; file already exists.`
49
+ );
50
+ }
51
+ }
52
+ writeFileText(output, data);
53
+ logger.info(`Localization strings have been written to ${output}`);
54
+ } else {
55
+ logger.error("Missing auth information");
56
+ }
57
+ }
@@ -1,7 +1,7 @@
1
+ import L from "lodash";
1
2
  import { ChecksumBundle, ProjectMetaBundle } from "../api";
2
3
  import { logger } from "../deps";
3
4
  import { formatScript, tsxToJsx } from "../utils/code-utils";
4
- import L from "lodash";
5
5
  import {
6
6
  PlasmicContext,
7
7
  ProjectConfig,
@@ -41,7 +41,7 @@ export async function syncGlobalContexts(
41
41
  context,
42
42
  resourcePath,
43
43
  projectMeta.globalContextBundle.contextModule,
44
- { force: false }
44
+ { force: true }
45
45
  );
46
46
  projectConfig.globalContextsFilePath = resourcePath;
47
47
  const fl = projectLock.fileLocks.find(
@@ -75,21 +75,72 @@ export async function watchProjects(
75
75
  socket.on("error", (data: any) => {
76
76
  reject(new HandledError(data));
77
77
  });
78
- socket.on("update", async (data: any) => {
79
- // Just run syncProjects() for now when any project has been updated
80
- // Note on the 'updated to revision' part: this is parsed by the
81
- // loader package to know that we finished updating the components.
82
- await sync(syncOpts, syncMetadata);
83
- logger.info(
84
- `[${moment().format("HH:mm:ss")}] Project ${
85
- data.projectId
86
- } updated to revision ${data.revisionNum}`
87
- );
78
+ socket.on(
79
+ "update",
80
+ asyncOneAtATime(async (data: any) => {
81
+ // Just run syncProjects() for now when any project has been updated
82
+ // Note on the 'updated to revision' part: this is parsed by the
83
+ // loader package to know that we finished updating the components.
84
+ await sync(syncOpts, syncMetadata);
85
+ logger.info(
86
+ `[${moment().format("HH:mm:ss")}] Project ${
87
+ data.projectId
88
+ } updated to revision ${data.revisionNum}`
89
+ );
88
90
 
89
- onProjectUpdate?.();
90
- });
91
+ onProjectUpdate?.();
92
+ }, true)
93
+ );
91
94
  });
92
95
 
93
96
  logger.info(`Watching projects ${latestProjects} ...`);
94
97
  return promise;
95
98
  }
99
+
100
+ /**
101
+ * Throttle invocations of a function to allow a single outstanding invocation
102
+ * at a time.
103
+ *
104
+ * But, has a buffer of size one, so that after the current invocation
105
+ * completes, it calls the last attempted invocation.
106
+ *
107
+ * Other invocations that get evicted from the buffer get returned bounceValue
108
+ * upon eviction.
109
+ */
110
+ type AsyncCallable = (...args: any[]) => Promise<any>;
111
+
112
+ function asyncOneAtATime(f: AsyncCallable, bounceValue: any): AsyncCallable {
113
+ interface CallInfo {
114
+ args: any[];
115
+ resolve: any;
116
+ reject: any;
117
+ }
118
+ let waitingCall: CallInfo | undefined = undefined,
119
+ currentPromise: Promise<any> | undefined = undefined;
120
+ function invoke({ args, resolve, reject }: CallInfo) {
121
+ const onCompletion = () => {
122
+ currentPromise = undefined;
123
+ if (waitingCall) {
124
+ invoke(waitingCall);
125
+ waitingCall = undefined;
126
+ }
127
+ };
128
+ currentPromise = f(...args);
129
+ currentPromise.then(onCompletion, onCompletion);
130
+ currentPromise.then(resolve, reject);
131
+ }
132
+ return (...args: any[]) => {
133
+ return new Promise((resolve, reject) => {
134
+ if (!currentPromise) {
135
+ // Free to proceed.
136
+ invoke({ args, resolve, reject });
137
+ } else {
138
+ // Evict current waiter, and enqueue self.
139
+ if (waitingCall) {
140
+ waitingCall.resolve(bounceValue);
141
+ }
142
+ waitingCall = { args, resolve, reject };
143
+ }
144
+ });
145
+ };
146
+ }
package/src/api.ts CHANGED
@@ -286,6 +286,20 @@ export class PlasmicApi {
286
286
  return result.data as ProjectMetaInfo;
287
287
  }
288
288
 
289
+ async genLocalizationStrings(
290
+ projects: readonly string[],
291
+ format: "po" | "json" | "lingui"
292
+ ) {
293
+ const result = await this.get(
294
+ `${
295
+ this.codegenHost
296
+ }/api/v1/localization/gen-texts?format=${format}&preview=true&${projects
297
+ .map((p) => `projectId=${p}`)
298
+ .join("&")}`
299
+ );
300
+ return result.data as string;
301
+ }
302
+
289
303
  async uploadBundle(
290
304
  projectId: string,
291
305
  bundleName: string,
@@ -393,6 +407,27 @@ export class PlasmicApi {
393
407
  }
394
408
  }
395
409
 
410
+ private async get(url: string, rethrowAppError?: boolean) {
411
+ try {
412
+ return await axios.get(url, {
413
+ headers: this.makeHeaders(),
414
+ });
415
+ } catch (e) {
416
+ const error = e as AxiosError;
417
+ const errorMsg = this.makeErrorMessage(error);
418
+
419
+ if (rethrowAppError) {
420
+ throw new AppServerError(errorMsg);
421
+ }
422
+
423
+ if (!errorMsg) {
424
+ throw e;
425
+ }
426
+
427
+ throw new HandledError(errorMsg);
428
+ }
429
+ }
430
+
396
431
  private makeErrorMessage(error: AxiosError) {
397
432
  const response = error.response;
398
433
  if (!response) {
package/src/index.ts CHANGED
@@ -6,6 +6,10 @@ import * as auth from "./actions/auth";
6
6
  import { fixImports, FixImportsArgs } from "./actions/fix-imports";
7
7
  import { InfoArgs, printProjectInfo } from "./actions/info";
8
8
  import { getYargsOption, InitArgs, initPlasmic } from "./actions/init";
9
+ import {
10
+ localizationStrings,
11
+ LocalizationStringsArgs,
12
+ } from "./actions/localization-strings";
9
13
  import * as projectToken from "./actions/project-token";
10
14
  import { sync, SyncArgs } from "./actions/sync";
11
15
  import { UploadBundleArgs, uploadJsBundle } from "./actions/upload-bundle";
@@ -226,6 +230,40 @@ yargs
226
230
  }),
227
231
  (argv) => handleError(projectToken.projectToken(argv))
228
232
  )
233
+ .command<LocalizationStringsArgs>(
234
+ "localization-strings",
235
+ false,
236
+ (yargs) =>
237
+ yargs
238
+ .option("projects", {
239
+ alias: "p",
240
+ describe:
241
+ "One or more projects to generate localization strings, separated by comma. Version constraints can be specified using @. Example: projectid, projectid@>=version",
242
+ type: "array",
243
+ })
244
+ .option("host", {
245
+ describe: "Plasmic host to use",
246
+ type: "string",
247
+ default: "https://studio.plasmic.app",
248
+ })
249
+ .option("format", {
250
+ describe: 'Output format. Either "json", "po" or "lingui"',
251
+ type: "string",
252
+ choices: ["json", "po", "lingui"],
253
+ default: "json",
254
+ })
255
+ .option("output", {
256
+ alias: "o",
257
+ describe: "Output file",
258
+ type: "string",
259
+ })
260
+ .option("force-overwrite", {
261
+ type: "boolean",
262
+ describe: "Overwrite the output file.",
263
+ default: false,
264
+ }),
265
+ (argv) => handleError(localizationStrings(argv))
266
+ )
229
267
  .demandCommand()
230
268
  .strict()
231
269
  .help("h")
@@ -1,2 +0,0 @@
1
- import { PlasmicConfig } from "../utils/config-utils";
2
- export declare function ensureImportModuleType(config: PlasmicConfig): PlasmicConfig;
@@ -1,12 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ensureImportModuleType = void 0;
4
- function ensureImportModuleType(config) {
5
- for (const project of config.projects) {
6
- for (const component of project.components) {
7
- component.importSpec.moduleType = "local";
8
- }
9
- }
10
- return config;
11
- }
12
- exports.ensureImportModuleType = ensureImportModuleType;