@canva/cli 0.0.1-beta.9 → 1.0.0
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/README.md +131 -31
- package/cli.js +841 -437
- package/lib/cjs/index.cjs +11 -0
- package/lib/esm/index.mjs +11 -0
- package/package.json +19 -4
- package/templates/base/backend/routers/auth.ts +4 -4
- package/templates/base/backend/routers/oauth.ts +3 -3
- package/templates/base/eslint.config.mjs +2 -25
- package/templates/base/package.json +26 -23
- package/templates/base/scripts/ssl/ssl.ts +1 -1
- package/templates/base/scripts/start/app_runner.ts +42 -5
- package/templates/base/scripts/start/context.ts +12 -6
- package/templates/base/scripts/start/start.ts +12 -1
- package/templates/base/scripts/start/tests/start.tests.ts +61 -0
- package/templates/base/utils/backend/base_backend/create.ts +3 -3
- package/templates/base/utils/backend/bearer_middleware/bearer_middleware.ts +2 -2
- package/templates/base/utils/backend/jwt_middleware/jwt_middleware.ts +4 -4
- package/templates/base/utils/backend/jwt_middleware/tests/jwt_middleware.tests.ts +1 -1
- package/templates/base/utils/use_add_element.ts +1 -1
- package/templates/base/utils/use_feature_support.ts +1 -1
- package/templates/{gen_ai/webpack.config.cjs → base/webpack.config.ts} +50 -68
- package/templates/common/.env.template +3 -3
- package/templates/common/README.md +16 -17
- package/templates/common/jest.config.mjs +1 -1
- package/templates/common/jest.setup.ts +1 -0
- package/templates/common/utils/backend/base_backend/create.ts +3 -3
- package/templates/common/utils/backend/jwt_middleware/jwt_middleware.ts +4 -4
- package/templates/common/utils/backend/jwt_middleware/tests/jwt_middleware.tests.ts +1 -1
- package/templates/common/utils/tests/table_wrapper.tests.ts +1 -1
- package/templates/common/utils/use_add_element.ts +1 -1
- package/templates/common/utils/use_feature_support.ts +1 -1
- package/templates/dam/backend/routers/dam.ts +24 -20
- package/templates/dam/backend/server.ts +1 -1
- package/templates/dam/canva-app.json +16 -0
- package/templates/dam/eslint.config.mjs +2 -28
- package/templates/dam/package.json +38 -41
- package/templates/dam/scripts/ssl/ssl.ts +1 -1
- package/templates/dam/scripts/start/app_runner.ts +42 -5
- package/templates/dam/scripts/start/context.ts +12 -6
- package/templates/dam/scripts/start/start.ts +12 -1
- package/templates/dam/scripts/start/tests/start.tests.ts +61 -0
- package/templates/dam/src/app.tsx +24 -3
- package/templates/dam/src/config.ts +212 -87
- package/templates/dam/src/index.tsx +1 -1
- package/templates/dam/utils/backend/base_backend/create.ts +3 -3
- package/templates/dam/utils/backend/jwt_middleware/jwt_middleware.ts +4 -4
- package/templates/dam/utils/backend/jwt_middleware/tests/jwt_middleware.tests.ts +1 -1
- package/templates/{base/webpack.config.cjs → dam/webpack.config.ts} +50 -68
- package/templates/data_connector/README.md +84 -0
- package/templates/data_connector/canva-app.json +21 -0
- package/templates/data_connector/declarations/declarations.d.ts +29 -0
- package/templates/data_connector/eslint.config.mjs +14 -0
- package/templates/data_connector/package.json +91 -0
- package/templates/data_connector/scripts/ssl/ssl.ts +131 -0
- package/templates/data_connector/scripts/start/app_runner.ts +201 -0
- package/templates/data_connector/scripts/start/context.ts +171 -0
- package/templates/data_connector/scripts/start/start.ts +46 -0
- package/templates/data_connector/scripts/start/tests/start.tests.ts +61 -0
- package/templates/data_connector/src/api/connect_client.ts +6 -0
- package/templates/data_connector/src/api/data_source.ts +96 -0
- package/templates/data_connector/src/api/data_sources/designs.tsx +296 -0
- package/templates/data_connector/src/api/data_sources/index.ts +4 -0
- package/templates/data_connector/src/api/data_sources/templates.tsx +329 -0
- package/templates/data_connector/src/api/fetch_data_table.ts +55 -0
- package/templates/data_connector/src/api/index.ts +4 -0
- package/templates/data_connector/src/api/oauth.ts +8 -0
- package/templates/data_connector/src/api/tests/data_source.test.tsx +99 -0
- package/templates/data_connector/src/app.tsx +20 -0
- package/templates/data_connector/src/components/app_error.tsx +15 -0
- package/templates/data_connector/src/components/footer.tsx +26 -0
- package/templates/data_connector/src/components/header.tsx +40 -0
- package/templates/data_connector/src/components/index.ts +3 -0
- package/templates/data_connector/src/components/inputs/messages.tsx +99 -0
- package/templates/data_connector/src/components/inputs/search_filter.tsx +108 -0
- package/templates/data_connector/src/components/inputs/select_field.tsx +26 -0
- package/templates/data_connector/src/context/app_context.tsx +124 -0
- package/templates/data_connector/src/context/index.ts +2 -0
- package/templates/data_connector/src/context/use_app_context.ts +17 -0
- package/templates/data_connector/src/entrypoint.tsx +70 -0
- package/templates/data_connector/src/home.tsx +21 -0
- package/templates/data_connector/src/index.tsx +68 -0
- package/templates/data_connector/src/pages/data_source_config.tsx +9 -0
- package/templates/data_connector/src/pages/error.tsx +37 -0
- package/templates/data_connector/src/pages/index.ts +4 -0
- package/templates/data_connector/src/pages/login.tsx +145 -0
- package/templates/data_connector/src/pages/select_source.tsx +24 -0
- package/templates/data_connector/src/routes/index.ts +2 -0
- package/templates/data_connector/src/routes/protected_route.tsx +25 -0
- package/templates/data_connector/src/routes/routes.tsx +46 -0
- package/templates/data_connector/src/utils/data_params.ts +17 -0
- package/templates/data_connector/src/utils/data_table.ts +115 -0
- package/templates/data_connector/src/utils/fetch_result.ts +36 -0
- package/templates/data_connector/src/utils/index.ts +2 -0
- package/templates/data_connector/src/utils/tests/data_table.test.ts +133 -0
- package/templates/data_connector/styles/components.css +38 -0
- package/templates/data_connector/tsconfig.json +54 -0
- package/templates/{dam/webpack.config.cjs → data_connector/webpack.config.ts} +50 -68
- package/templates/gen_ai/backend/routers/image.ts +8 -8
- package/templates/gen_ai/backend/server.ts +1 -1
- package/templates/gen_ai/canva-app.json +20 -0
- package/templates/gen_ai/eslint.config.mjs +2 -25
- package/templates/gen_ai/package.json +42 -43
- package/templates/gen_ai/scripts/ssl/ssl.ts +1 -1
- package/templates/gen_ai/scripts/start/app_runner.ts +42 -5
- package/templates/gen_ai/scripts/start/context.ts +12 -6
- package/templates/gen_ai/scripts/start/start.ts +12 -1
- package/templates/gen_ai/scripts/start/tests/start.tests.ts +61 -0
- package/templates/gen_ai/src/app.tsx +3 -3
- package/templates/gen_ai/src/components/footer.tsx +4 -4
- package/templates/gen_ai/src/components/image_grid.tsx +1 -1
- package/templates/gen_ai/src/components/loading_results.tsx +8 -8
- package/templates/gen_ai/src/components/prompt_input.tsx +3 -3
- package/templates/gen_ai/src/components/remaining_credits.tsx +1 -1
- package/templates/gen_ai/src/components/report_box.tsx +1 -1
- package/templates/gen_ai/src/components/tests/remaining_credit.tests.tsx +2 -2
- package/templates/gen_ai/src/home.tsx +2 -2
- package/templates/gen_ai/src/pages/error.tsx +2 -2
- package/templates/gen_ai/src/pages/results.tsx +1 -1
- package/templates/gen_ai/utils/backend/base_backend/create.ts +3 -3
- package/templates/gen_ai/utils/backend/bearer_middleware/bearer_middleware.ts +2 -2
- package/templates/{hello_world/webpack.config.cjs → gen_ai/webpack.config.ts} +50 -68
- package/templates/hello_world/canva-app.json +16 -0
- package/templates/hello_world/eslint.config.mjs +2 -25
- package/templates/hello_world/package.json +35 -38
- package/templates/hello_world/scripts/copy_env.ts +10 -0
- package/templates/hello_world/scripts/ssl/ssl.ts +1 -1
- package/templates/hello_world/scripts/start/app_runner.ts +42 -5
- package/templates/hello_world/scripts/start/context.ts +12 -6
- package/templates/hello_world/scripts/start/start.ts +12 -1
- package/templates/hello_world/scripts/start/tests/start.tests.ts +61 -0
- package/templates/hello_world/src/app.tsx +1 -1
- package/templates/hello_world/src/index.tsx +1 -1
- package/templates/hello_world/src/tests/__snapshots__/app.tests.tsx.snap +5 -5
- package/templates/hello_world/src/tests/app.tests.tsx +3 -3
- package/templates/hello_world/utils/use_add_element.ts +1 -1
- package/templates/hello_world/utils/use_feature_support.ts +1 -1
- package/templates/hello_world/webpack.config.ts +254 -0
- package/templates/optional/.cursor/mcp.json +8 -0
- package/templates/optional/.vscode/mcp.json +9 -0
- package/templates/optional/AGENTS.md +76 -0
- package/templates/optional/CLAUDE.md +76 -0
- package/templates/common/conf/eslint-general.mjs +0 -303
- package/templates/common/conf/eslint-i18n.mjs +0 -41
- package/templates/common/conf/eslint-local-i18n-rules/index.mjs +0 -181
- /package/templates/base/scripts/{copy-env.ts → copy_env.ts} +0 -0
- /package/templates/dam/scripts/{copy-env.ts → copy_env.ts} +0 -0
- /package/templates/{gen_ai/scripts/copy-env.ts → data_connector/scripts/copy_env.ts} +0 -0
- /package/templates/{hello_world/scripts/copy-env.ts → gen_ai/scripts/copy_env.ts} +0 -0
- /package/templates/{common → optional}/.vscode/extensions.json +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
|
+
import debug from "debug";
|
|
2
3
|
import * as express from "express";
|
|
4
|
+
import type { NextFunction, Request, Response } from "express";
|
|
5
|
+
import * as fs from "fs";
|
|
3
6
|
import * as http from "http";
|
|
4
7
|
import * as https from "https";
|
|
5
|
-
import * as fs from "fs";
|
|
6
|
-
import type { Request, Response, NextFunction } from "express";
|
|
7
|
-
import debug from "debug";
|
|
8
8
|
|
|
9
9
|
const serverDebug = debug("server");
|
|
10
10
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
import * as chalk from "chalk";
|
|
3
3
|
import * as debug from "debug";
|
|
4
|
-
import type { Request, Response
|
|
5
|
-
import * as jwt from "jsonwebtoken";
|
|
6
|
-
import { JwksClient, SigningKeyNotFoundError } from "jwks-rsa";
|
|
4
|
+
import type { NextFunction, Request, Response } from "express";
|
|
7
5
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
8
6
|
import Express from "express-serve-static-core";
|
|
7
|
+
import * as jwt from "jsonwebtoken";
|
|
8
|
+
import { JwksClient, SigningKeyNotFoundError } from "jwks-rsa";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Prefix your start command with `DEBUG=express:middleware:jwt` to enable debug logging
|
|
@@ -110,7 +110,7 @@ export function createJwtMiddleware(
|
|
|
110
110
|
return sendUnauthorizedResponse(res);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
req
|
|
113
|
+
req["canva"] = {
|
|
114
114
|
appId: payload.aud,
|
|
115
115
|
brandId: payload.brandId,
|
|
116
116
|
userId: payload.userId,
|
|
@@ -446,7 +446,7 @@ describe("createJwtMiddleware", () => {
|
|
|
446
446
|
expect(res.status).not.toHaveBeenCalled();
|
|
447
447
|
expect(res.json).not.toHaveBeenCalled();
|
|
448
448
|
|
|
449
|
-
expect(req
|
|
449
|
+
expect(req["canva"]).toEqual({
|
|
450
450
|
userId: FAKE_USER_ID,
|
|
451
451
|
brandId: FAKE_BRAND_ID,
|
|
452
452
|
appId: FAKE_APP_ID,
|
|
@@ -7,9 +7,9 @@ import type {
|
|
|
7
7
|
VideoElement,
|
|
8
8
|
} from "@canva/design";
|
|
9
9
|
import { addElementAtCursor, addElementAtPoint } from "@canva/design";
|
|
10
|
-
import { useFeatureSupport } from "./use_feature_support";
|
|
11
10
|
import { features } from "@canva/platform";
|
|
12
11
|
import { useEffect, useState } from "react";
|
|
12
|
+
import { useFeatureSupport } from "./use_feature_support";
|
|
13
13
|
|
|
14
14
|
type AddElementParams =
|
|
15
15
|
| ImageElement
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type { Container, Resource } from "@canva/app-components";
|
|
2
2
|
import * as crypto from "crypto";
|
|
3
|
-
import
|
|
4
|
-
Container,
|
|
5
|
-
FindResourcesRequest,
|
|
6
|
-
Resource,
|
|
7
|
-
} from "@canva/app-components";
|
|
3
|
+
import * as express from "express";
|
|
8
4
|
|
|
9
5
|
/**
|
|
10
6
|
* Generates a unique hash for a url.
|
|
@@ -21,35 +17,43 @@ export async function generateHash(message: string) {
|
|
|
21
17
|
}
|
|
22
18
|
|
|
23
19
|
const imageUrls = [
|
|
24
|
-
"https://
|
|
25
|
-
"https://
|
|
26
|
-
"https://
|
|
27
|
-
"https://
|
|
28
|
-
"https://
|
|
20
|
+
"https://images.pexels.com/photos/1495580/pexels-photo-1495580.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
|
|
21
|
+
"https://images.pexels.com/photos/3943197/pexels-photo-3943197.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
|
|
22
|
+
"https://images.pexels.com/photos/7195267/pexels-photo-7195267.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
|
|
23
|
+
"https://images.pexels.com/photos/2904142/pexels-photo-2904142.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
|
|
24
|
+
"https://images.pexels.com/photos/5403478/pexels-photo-5403478.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
|
|
29
25
|
];
|
|
30
|
-
|
|
31
26
|
export const createDamRouter = () => {
|
|
32
27
|
const router = express.Router();
|
|
33
28
|
|
|
34
29
|
/**
|
|
35
30
|
* This endpoint returns the data for your app.
|
|
36
|
-
*
|
|
37
|
-
* TODO: you must change this path to `/api/resources/find` before
|
|
38
|
-
* submitting to ensure a user is authenticated
|
|
39
31
|
*/
|
|
40
32
|
router.post("/resources/find", async (req, res) => {
|
|
41
33
|
// You should modify these lines to return data from your
|
|
42
34
|
// digital asset manager (DAM) based on the findResourcesRequest
|
|
43
|
-
const
|
|
44
|
-
|
|
35
|
+
const {
|
|
36
|
+
types,
|
|
37
|
+
continuation,
|
|
38
|
+
locale,
|
|
39
|
+
// other available fields from the `FindResourcesRequest`
|
|
40
|
+
// containerTypes,
|
|
41
|
+
// limit,
|
|
42
|
+
// filters,
|
|
43
|
+
// query,
|
|
44
|
+
// sort,
|
|
45
|
+
// tab,
|
|
46
|
+
// containerId,
|
|
47
|
+
// parentContainerType,
|
|
48
|
+
} = req.body;
|
|
45
49
|
|
|
46
50
|
let resources: Resource[] = [];
|
|
47
51
|
if (types.includes("IMAGE")) {
|
|
48
52
|
resources = await Promise.all(
|
|
49
53
|
Array.from({ length: 40 }, async (_, i) => ({
|
|
50
|
-
id: await generateHash(
|
|
54
|
+
id: await generateHash(i + ""),
|
|
51
55
|
mimeType: "image/jpeg",
|
|
52
|
-
name:
|
|
56
|
+
name: `My new thing in ${locale}`, // Use the `locale` value from the request if your backend supports i18n
|
|
53
57
|
type: "IMAGE",
|
|
54
58
|
thumbnail: {
|
|
55
59
|
url: imageUrls[i % imageUrls.length],
|
|
@@ -78,7 +82,7 @@ export const createDamRouter = () => {
|
|
|
78
82
|
|
|
79
83
|
res.send({
|
|
80
84
|
resources,
|
|
81
|
-
continuation: +(
|
|
85
|
+
continuation: +(continuation || 0) + 1,
|
|
82
86
|
});
|
|
83
87
|
});
|
|
84
88
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://www.canva.dev/schemas/app/v1/manifest-schema.json",
|
|
3
|
+
"manifest_schema_version": 1,
|
|
4
|
+
"runtime": {
|
|
5
|
+
"permissions": [
|
|
6
|
+
{
|
|
7
|
+
"name": "canva:design:content:read",
|
|
8
|
+
"type": "mandatory"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"name": "canva:design:content:write",
|
|
12
|
+
"type": "mandatory"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -1,17 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
import js from "@eslint/js";
|
|
4
|
-
import { FlatCompat } from "@eslint/eslintrc";
|
|
5
|
-
import general from "./conf/eslint-general.mjs";
|
|
6
|
-
// import i18n from "./conf/eslint-i18n.mjs";
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = path.dirname(__filename);
|
|
10
|
-
const compat = new FlatCompat({
|
|
11
|
-
baseDirectory: __dirname,
|
|
12
|
-
recommendedConfig: js.configs.recommended,
|
|
13
|
-
allConfig: js.configs.all,
|
|
14
|
-
});
|
|
1
|
+
import canvaPlugin from "@canva/app-eslint-plugin";
|
|
15
2
|
|
|
16
3
|
export default [
|
|
17
4
|
{
|
|
@@ -20,21 +7,8 @@ export default [
|
|
|
20
7
|
"**/dist",
|
|
21
8
|
"**/*.d.ts",
|
|
22
9
|
"**/*.d.tsx",
|
|
23
|
-
"**/sdk",
|
|
24
|
-
"**/internal",
|
|
25
10
|
"**/*.config.*",
|
|
26
11
|
],
|
|
27
12
|
},
|
|
28
|
-
...
|
|
29
|
-
"eslint:recommended",
|
|
30
|
-
"plugin:@typescript-eslint/recommended",
|
|
31
|
-
"plugin:@typescript-eslint/eslint-recommended",
|
|
32
|
-
"plugin:@typescript-eslint/strict",
|
|
33
|
-
"plugin:@typescript-eslint/stylistic",
|
|
34
|
-
"plugin:react/recommended",
|
|
35
|
-
"plugin:jest/recommended",
|
|
36
|
-
),
|
|
37
|
-
...general,
|
|
38
|
-
// This template is not yet translated, enable these rules when it is
|
|
39
|
-
// ...i18n,
|
|
13
|
+
...canvaPlugin.configs.apps,
|
|
40
14
|
];
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"description": "An example app leveraging @canva/app-components to develop a digital asset management (DAM) app.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"extract": "formatjs extract \"src/**/*.{ts,tsx}\" --out-file dist/messages_en.json",
|
|
7
|
-
"build": "webpack --config webpack.config.
|
|
7
|
+
"build": "webpack --config webpack.config.ts --mode production && npm run extract",
|
|
8
8
|
"format": "prettier '**/*.{css,ts,tsx}' --no-config --write",
|
|
9
9
|
"format:check": "prettier '**/*.{css,ts,tsx}' --no-config --check --ignore-path",
|
|
10
10
|
"format:file": "prettier $1 --no-config --write",
|
|
@@ -12,19 +12,20 @@
|
|
|
12
12
|
"lint:fix": "eslint . --fix",
|
|
13
13
|
"lint:types": "tsc",
|
|
14
14
|
"start": "ts-node ./scripts/start/start.ts",
|
|
15
|
+
"start:preview": "npm run start -- --preview",
|
|
15
16
|
"test": "jest --no-cache",
|
|
16
17
|
"test:watch": "jest --watchAll",
|
|
17
|
-
"postinstall": "ts-node ./scripts/
|
|
18
|
+
"postinstall": "ts-node ./scripts/copy_env.ts"
|
|
18
19
|
},
|
|
19
20
|
"dependencies": {
|
|
20
|
-
"@canva/app-components": "^1.
|
|
21
|
-
"@canva/app-i18n-kit": "^1.0.
|
|
22
|
-
"@canva/app-ui-kit": "^4.
|
|
23
|
-
"@canva/asset": "^2.1
|
|
24
|
-
"@canva/design": "^2.
|
|
21
|
+
"@canva/app-components": "^1.3.1",
|
|
22
|
+
"@canva/app-i18n-kit": "^1.0.3",
|
|
23
|
+
"@canva/app-ui-kit": "^4.10.0",
|
|
24
|
+
"@canva/asset": "^2.2.1",
|
|
25
|
+
"@canva/design": "^2.7.2",
|
|
25
26
|
"@canva/error": "^2.1.0",
|
|
26
|
-
"@canva/platform": "^2.
|
|
27
|
-
"@canva/user": "^2.1.
|
|
27
|
+
"@canva/platform": "^2.2.0",
|
|
28
|
+
"@canva/user": "^2.1.1",
|
|
28
29
|
"cookie-parser": "1.4.7",
|
|
29
30
|
"cors": "2.8.5",
|
|
30
31
|
"react": "18.3.1",
|
|
@@ -32,67 +33,63 @@
|
|
|
32
33
|
"react-intl": "6.8.7"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
|
-
"@eslint
|
|
36
|
-
"@
|
|
37
|
-
"@formatjs/cli": "6.
|
|
38
|
-
"@formatjs/ts-transformer": "3.
|
|
39
|
-
"@ngrok/ngrok": "1.
|
|
40
|
-
"@pmmmwh/react-refresh-webpack-plugin": "0.
|
|
36
|
+
"@canva/app-eslint-plugin": "^1.0.0-beta.3",
|
|
37
|
+
"@canva/cli": ">= 0.0.1-beta.13",
|
|
38
|
+
"@formatjs/cli": "6.7.2",
|
|
39
|
+
"@formatjs/ts-transformer": "3.14.0",
|
|
40
|
+
"@ngrok/ngrok": "1.5.1",
|
|
41
|
+
"@pmmmwh/react-refresh-webpack-plugin": "0.6.1",
|
|
41
42
|
"@svgr/webpack": "8.1.0",
|
|
42
|
-
"@testing-library/react": "16.
|
|
43
|
-
"@types/cors": "2.8.
|
|
43
|
+
"@testing-library/react": "16.3.0",
|
|
44
|
+
"@types/cors": "2.8.19",
|
|
44
45
|
"@types/debug": "4.1.12",
|
|
45
46
|
"@types/express": "4.17.21",
|
|
46
|
-
"@types/express-serve-static-core": "
|
|
47
|
+
"@types/express-serve-static-core": "5.0.6",
|
|
47
48
|
"@types/jest": "29.5.14",
|
|
48
|
-
"@types/jsonwebtoken": "9.0.
|
|
49
|
-
"@types/node": "20.
|
|
49
|
+
"@types/jsonwebtoken": "9.0.10",
|
|
50
|
+
"@types/node": "20.19.2",
|
|
50
51
|
"@types/node-fetch": "2.6.12",
|
|
51
52
|
"@types/node-forge": "1.3.11",
|
|
52
53
|
"@types/nodemon": "1.19.6",
|
|
53
54
|
"@types/prompts": "2.4.9",
|
|
54
55
|
"@types/react": "18.3.12",
|
|
55
56
|
"@types/react-dom": "18.3.1",
|
|
56
|
-
"@types/webpack-env": "1.18.
|
|
57
|
-
"@typescript-eslint/eslint-plugin": "8.18.0",
|
|
58
|
-
"@typescript-eslint/parser": "8.18.0",
|
|
57
|
+
"@types/webpack-env": "1.18.8",
|
|
59
58
|
"chalk": "4.1.2",
|
|
60
59
|
"cli-table3": "0.6.5",
|
|
61
60
|
"css-loader": "7.1.2",
|
|
62
61
|
"css-modules-typescript-loader": "4.0.1",
|
|
63
|
-
"cssnano": "7.0.
|
|
64
|
-
"debug": "4.4.
|
|
65
|
-
"dotenv": "16.
|
|
66
|
-
"
|
|
67
|
-
"eslint-plugin-formatjs": "5.2.8",
|
|
68
|
-
"eslint-plugin-jest": "28.9.0",
|
|
69
|
-
"eslint-plugin-react": "7.37.2",
|
|
70
|
-
"exponential-backoff": "3.1.1",
|
|
62
|
+
"cssnano": "7.0.7",
|
|
63
|
+
"debug": "4.4.1",
|
|
64
|
+
"dotenv": "16.6.0",
|
|
65
|
+
"exponential-backoff": "3.1.2",
|
|
71
66
|
"express": "4.21.2",
|
|
72
67
|
"express-basic-auth": "1.2.1",
|
|
73
68
|
"jest": "29.7.0",
|
|
74
69
|
"jest-css-modules-transform": "4.4.2",
|
|
75
70
|
"jest-environment-jsdom": "29.7.0",
|
|
76
71
|
"jsonwebtoken": "9.0.2",
|
|
77
|
-
"jwks-rsa": "3.
|
|
72
|
+
"jwks-rsa": "3.2.0",
|
|
78
73
|
"mini-css-extract-plugin": "2.9.2",
|
|
79
74
|
"node-fetch": "3.3.2",
|
|
80
75
|
"node-forge": "1.3.1",
|
|
81
76
|
"nodemon": "3.0.1",
|
|
77
|
+
"open": "8.4.2",
|
|
82
78
|
"postcss-loader": "8.1.1",
|
|
83
|
-
"prettier": "3.
|
|
79
|
+
"prettier": "3.6.2",
|
|
84
80
|
"prompts": "2.4.2",
|
|
85
|
-
"react-refresh": "0.
|
|
81
|
+
"react-refresh": "0.17.0",
|
|
86
82
|
"style-loader": "4.0.0",
|
|
87
|
-
"terser-webpack-plugin": "5.3.
|
|
88
|
-
"
|
|
89
|
-
"ts-
|
|
83
|
+
"terser-webpack-plugin": "5.3.14",
|
|
84
|
+
"tree-kill": "1.2.2",
|
|
85
|
+
"ts-jest": "29.4.0",
|
|
86
|
+
"ts-loader": "9.5.2",
|
|
90
87
|
"ts-node": "10.9.2",
|
|
91
|
-
"typescript": "5.
|
|
88
|
+
"typescript": "5.8.2",
|
|
92
89
|
"url-loader": "4.1.1",
|
|
93
|
-
"webpack": "5.
|
|
94
|
-
"webpack-cli": "
|
|
95
|
-
"webpack-dev-server": "5.
|
|
90
|
+
"webpack": "5.99.9",
|
|
91
|
+
"webpack-cli": "6.0.1",
|
|
92
|
+
"webpack-dev-server": "5.2.2",
|
|
96
93
|
"yargs": "17.7.2"
|
|
97
94
|
}
|
|
98
95
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as crypto from "crypto";
|
|
2
|
+
import * as fs from "fs/promises";
|
|
2
3
|
import { pki } from "node-forge";
|
|
3
4
|
import * as path from "path";
|
|
4
|
-
import * as fs from "fs/promises";
|
|
5
5
|
|
|
6
6
|
const SSL_CERT_DIR = path.resolve(process.cwd(), "..", "..", ".ssl");
|
|
7
7
|
const CERT_FILE = path.resolve(SSL_CERT_DIR, "certificate.pem");
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
|
-
import
|
|
3
|
-
import * as chalk from "chalk";
|
|
4
|
-
import { buildConfig } from "../../webpack.config.cjs";
|
|
2
|
+
import { generatePreviewUrl } from "@canva/cli";
|
|
5
3
|
import * as ngrok from "@ngrok/ngrok";
|
|
6
|
-
import * as
|
|
4
|
+
import * as chalk from "chalk";
|
|
7
5
|
import * as Table from "cli-table3";
|
|
6
|
+
import * as nodemon from "nodemon";
|
|
7
|
+
import * as open from "open";
|
|
8
8
|
import * as webpack from "webpack";
|
|
9
9
|
import * as WebpackDevServer from "webpack-dev-server";
|
|
10
|
+
import { buildConfig } from "../../webpack.config";
|
|
10
11
|
import type { Certificate } from "../ssl/ssl";
|
|
11
12
|
import { createOrRetrieveCertificate } from "../ssl/ssl";
|
|
13
|
+
import type { Context } from "./context";
|
|
12
14
|
|
|
13
15
|
export const infoChalk = chalk.blue.bold;
|
|
14
16
|
export const warnChalk = chalk.bgYellow.bold;
|
|
@@ -46,12 +48,18 @@ export class AppRunner {
|
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
const table = new Table(
|
|
51
|
+
const table = new Table({
|
|
52
|
+
colWidths: [30, 80],
|
|
53
|
+
wordWrap: true,
|
|
54
|
+
wrapOnWordBoundary: true,
|
|
55
|
+
});
|
|
50
56
|
|
|
51
57
|
const server = await this.runWebpackDevServer(ctx, table, cert);
|
|
52
58
|
|
|
53
59
|
await this.maybeRunBackendServer(ctx, table, cert, server);
|
|
54
60
|
|
|
61
|
+
await this.generateAndOpenPreviewUrl(ctx.openPreview, table);
|
|
62
|
+
|
|
55
63
|
console.log(table.toString(), "\n");
|
|
56
64
|
|
|
57
65
|
console.log(
|
|
@@ -161,4 +169,33 @@ export class AppRunner {
|
|
|
161
169
|
|
|
162
170
|
return server;
|
|
163
171
|
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Calls the Canva CLI to generate a preview URL for the app
|
|
175
|
+
*/
|
|
176
|
+
private readonly generateAndOpenPreviewUrl = async (
|
|
177
|
+
openPreview: boolean,
|
|
178
|
+
table: Table.Table,
|
|
179
|
+
) => {
|
|
180
|
+
const previewCellHeader = { content: "Preview your app in Canva" };
|
|
181
|
+
|
|
182
|
+
const generatePreviewResult = await generatePreviewUrl();
|
|
183
|
+
|
|
184
|
+
if (!generatePreviewResult.success) {
|
|
185
|
+
table.push([
|
|
186
|
+
previewCellHeader,
|
|
187
|
+
{ content: warnChalk(generatePreviewResult.message) },
|
|
188
|
+
]);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
table.push([
|
|
193
|
+
previewCellHeader,
|
|
194
|
+
{ content: "Preview URL", href: generatePreviewResult.data },
|
|
195
|
+
]);
|
|
196
|
+
|
|
197
|
+
if (openPreview) {
|
|
198
|
+
open(generatePreviewResult.data);
|
|
199
|
+
}
|
|
200
|
+
};
|
|
164
201
|
}
|
|
@@ -3,8 +3,10 @@ import * as path from "path";
|
|
|
3
3
|
|
|
4
4
|
interface CliArgs {
|
|
5
5
|
example?: string;
|
|
6
|
-
useHttps
|
|
7
|
-
ngrok
|
|
6
|
+
useHttps: boolean;
|
|
7
|
+
ngrok: boolean;
|
|
8
|
+
preview: boolean;
|
|
9
|
+
overrideFrontendPort?: number;
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
interface EnvVars {
|
|
@@ -45,7 +47,7 @@ export class Context {
|
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
get ngrokEnabled() {
|
|
48
|
-
return
|
|
50
|
+
return this.args.ngrok;
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
get hmrEnabled() {
|
|
@@ -53,7 +55,7 @@ export class Context {
|
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
get httpsEnabled() {
|
|
56
|
-
return
|
|
58
|
+
return this.args.useHttps;
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
get frontendEntryPath() {
|
|
@@ -69,11 +71,11 @@ export class Context {
|
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
get frontendUrl() {
|
|
72
|
-
return `${this.protocol}://localhost:${this.
|
|
74
|
+
return `${this.protocol}://localhost:${this.frontendPort}`;
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
get frontendPort() {
|
|
76
|
-
return this.envVars.frontendPort;
|
|
78
|
+
return this.args.overrideFrontendPort || this.envVars.frontendPort;
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
get developerBackendEntryPath(): string | undefined {
|
|
@@ -117,6 +119,10 @@ export class Context {
|
|
|
117
119
|
return this.envVars.appId;
|
|
118
120
|
}
|
|
119
121
|
|
|
122
|
+
get openPreview(): boolean {
|
|
123
|
+
return this.args.preview;
|
|
124
|
+
}
|
|
125
|
+
|
|
120
126
|
private get protocol(): "https" | "http" {
|
|
121
127
|
return this.httpsEnabled ? "https" : "http";
|
|
122
128
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as yargs from "yargs";
|
|
3
|
-
import { AppRunner } from "./app_runner";
|
|
4
3
|
import { hideBin } from "yargs/helpers";
|
|
4
|
+
import { AppRunner } from "./app_runner";
|
|
5
5
|
import { Context } from "./context";
|
|
6
6
|
|
|
7
7
|
const appRunner = new AppRunner();
|
|
@@ -23,6 +23,17 @@ yargs(hideBin(process.argv))
|
|
|
23
23
|
default:
|
|
24
24
|
process.env.npm_config_use_https?.toLocaleLowerCase().trim() === "true",
|
|
25
25
|
})
|
|
26
|
+
.option("override-frontend-port", {
|
|
27
|
+
description:
|
|
28
|
+
"Port to run the local development server on. Overrides the frontend port set in the .env file.",
|
|
29
|
+
type: "number",
|
|
30
|
+
alias: "p",
|
|
31
|
+
})
|
|
32
|
+
.option("preview", {
|
|
33
|
+
description: "Open the app in Canva.",
|
|
34
|
+
type: "boolean",
|
|
35
|
+
default: false,
|
|
36
|
+
})
|
|
26
37
|
.command(
|
|
27
38
|
"$0",
|
|
28
39
|
"Starts local development",
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
|
|
3
|
+
import type { ChildProcess } from "child_process";
|
|
4
|
+
import { spawn } from "child_process";
|
|
5
|
+
import * as treeKill from "tree-kill";
|
|
6
|
+
|
|
7
|
+
describe("start script", () => {
|
|
8
|
+
let serverProcess: ChildProcess;
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
if (serverProcess?.pid && !serverProcess.exitCode) {
|
|
12
|
+
treeKill(serverProcess.pid);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should execute 'npm run start' and start a dev server", async () => {
|
|
17
|
+
const testServerPort = 8089;
|
|
18
|
+
serverProcess = await spawn(
|
|
19
|
+
`npm run start -- -p ${testServerPort} --no-preview`,
|
|
20
|
+
{
|
|
21
|
+
shell: true,
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
if (!serverProcess.pid) {
|
|
26
|
+
throw new Error("Unable to start server");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// wait for the server to start and collect output until it reports the running URL
|
|
30
|
+
let output = "";
|
|
31
|
+
await new Promise<void>((resolve, reject) => {
|
|
32
|
+
serverProcess.stdout?.on("data", (data) => {
|
|
33
|
+
output += data.toString();
|
|
34
|
+
if (output.includes("Development URL")) {
|
|
35
|
+
resolve();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
serverProcess.stderr?.on("data", (data) => {
|
|
40
|
+
console.error("Server process error: ", data.toString());
|
|
41
|
+
reject();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// timeout and fail test if the server hasn't correctly started in 10 seconds
|
|
45
|
+
setTimeout(() => {
|
|
46
|
+
if (serverProcess?.pid && !serverProcess.exitCode) {
|
|
47
|
+
treeKill(serverProcess.pid);
|
|
48
|
+
}
|
|
49
|
+
reject(new Error("Test timed out"));
|
|
50
|
+
}, 10000);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// check that the server is running and accessible
|
|
54
|
+
const expectedServerURL = `http://localhost:${testServerPort}`;
|
|
55
|
+
expect(output).toContain(expectedServerURL);
|
|
56
|
+
expect(serverProcess.exitCode).toBeNull();
|
|
57
|
+
|
|
58
|
+
// clean up
|
|
59
|
+
treeKill(serverProcess.pid);
|
|
60
|
+
}, 15000); // 15 seconds timeout to allow for the server to start
|
|
61
|
+
});
|
|
@@ -1,14 +1,35 @@
|
|
|
1
1
|
import { SearchableListView } from "@canva/app-components";
|
|
2
2
|
import { Box } from "@canva/app-ui-kit";
|
|
3
|
+
import "@canva/app-ui-kit/styles.css";
|
|
3
4
|
import { findResources } from "./adapter";
|
|
4
|
-
import {
|
|
5
|
+
import { useConfig } from "./config";
|
|
5
6
|
import * as styles from "./index.css";
|
|
6
|
-
import "@canva/app-ui-kit/styles.css";
|
|
7
7
|
|
|
8
8
|
export function App() {
|
|
9
|
+
const config = useConfig();
|
|
9
10
|
return (
|
|
10
11
|
<Box className={styles.rootWrapper}>
|
|
11
|
-
<SearchableListView
|
|
12
|
+
<SearchableListView
|
|
13
|
+
config={config}
|
|
14
|
+
findResources={findResources}
|
|
15
|
+
// TODO remove `saveExportedDesign` and `config.export` if your app does not support exporting the Canva design into an external platform
|
|
16
|
+
saveExportedDesign={(
|
|
17
|
+
exportedDesignUrl: string,
|
|
18
|
+
containerId: string | undefined,
|
|
19
|
+
designTitle: string | undefined,
|
|
20
|
+
) => {
|
|
21
|
+
// TODO update the function to save the design to your platform
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
setTimeout(() => {
|
|
24
|
+
// eslint-disable-next-line no-console
|
|
25
|
+
console.info(
|
|
26
|
+
`Saving file "${designTitle}" from ${exportedDesignUrl} to ${config.serviceName} container id: ${containerId}`,
|
|
27
|
+
);
|
|
28
|
+
resolve({ success: true });
|
|
29
|
+
}, 1000);
|
|
30
|
+
});
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
12
33
|
</Box>
|
|
13
34
|
);
|
|
14
35
|
}
|