@canva/cli 0.0.1-beta.3 → 0.0.1-beta.31
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 +218 -104
- package/cli.js +661 -394
- package/lib/cjs/index.cjs +5 -0
- package/lib/esm/index.mjs +5 -0
- package/package.json +19 -4
- package/templates/base/eslint.config.mjs +2 -27
- package/templates/base/package.json +31 -25
- package/templates/base/scripts/copy_env.ts +10 -0
- package/templates/base/scripts/start/app_runner.ts +39 -2
- package/templates/base/scripts/start/context.ts +12 -6
- package/templates/base/scripts/start/start.ts +11 -0
- package/templates/base/scripts/start/tests/start.tests.ts +61 -0
- package/templates/base/{webpack.config.cjs → webpack.config.ts} +46 -46
- package/templates/common/.cursor/mcp.json +8 -0
- package/templates/common/.gitignore.template +5 -6
- package/templates/common/.nvmrc +1 -0
- package/templates/common/.prettierrc +21 -0
- package/templates/common/.vscode/extensions.json +6 -0
- package/templates/common/.vscode/mcp.json +9 -0
- package/templates/common/README.md +4 -7
- package/templates/common/jest.config.mjs +29 -2
- package/templates/common/jest.setup.ts +20 -0
- package/templates/common/utils/backend/base_backend/create.ts +104 -0
- package/templates/common/utils/backend/jwt_middleware/index.ts +1 -0
- package/templates/common/utils/backend/jwt_middleware/jwt_middleware.ts +229 -0
- package/templates/common/utils/backend/jwt_middleware/tests/jwt_middleware.tests.ts +630 -0
- package/templates/common/utils/table_wrapper.ts +477 -0
- package/templates/common/utils/tests/table_wrapper.tests.ts +252 -0
- package/templates/common/utils/use_add_element.ts +48 -0
- package/templates/common/utils/use_feature_support.ts +28 -0
- package/templates/common/utils/use_overlay_hook.ts +74 -0
- package/templates/common/utils/use_selection_hook.ts +37 -0
- package/templates/dam/backend/routers/dam.ts +23 -19
- package/templates/dam/eslint.config.mjs +2 -28
- package/templates/dam/package.json +43 -39
- package/templates/dam/scripts/copy_env.ts +10 -0
- package/templates/dam/scripts/start/app_runner.ts +39 -2
- package/templates/dam/scripts/start/context.ts +12 -6
- package/templates/dam/scripts/start/start.ts +11 -0
- 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/{hello_world/webpack.config.cjs → dam/webpack.config.ts} +46 -46
- package/templates/data_connector/README.md +84 -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/copy_env.ts +10 -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 +69 -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/{gen_ai/webpack.config.cjs → data_connector/webpack.config.ts} +46 -46
- package/templates/gen_ai/README.md +1 -40
- package/templates/gen_ai/backend/routers/image.ts +11 -11
- package/templates/gen_ai/backend/server.ts +0 -7
- package/templates/gen_ai/eslint.config.mjs +2 -27
- package/templates/gen_ai/package.json +48 -42
- package/templates/gen_ai/scripts/copy_env.ts +10 -0
- package/templates/gen_ai/scripts/start/app_runner.ts +39 -2
- package/templates/gen_ai/scripts/start/context.ts +12 -6
- package/templates/gen_ai/scripts/start/start.ts +11 -0
- package/templates/gen_ai/scripts/start/tests/start.tests.ts +61 -0
- package/templates/gen_ai/src/api/api.ts +24 -79
- package/templates/gen_ai/src/app.tsx +16 -10
- package/templates/gen_ai/src/components/footer.messages.ts +0 -5
- package/templates/gen_ai/src/components/footer.tsx +7 -25
- package/templates/gen_ai/src/components/index.ts +0 -1
- package/templates/gen_ai/src/components/loading_results.tsx +4 -8
- package/templates/gen_ai/src/components/tests/remaining_credit.tests.tsx +43 -0
- package/templates/gen_ai/src/context/app_context.tsx +5 -33
- package/templates/gen_ai/src/context/context.messages.ts +1 -12
- package/templates/gen_ai/src/home.tsx +13 -0
- package/templates/gen_ai/src/index.tsx +2 -18
- package/templates/gen_ai/src/routes/routes.tsx +2 -2
- package/templates/{dam/webpack.config.cjs → gen_ai/webpack.config.ts} +46 -46
- package/templates/hello_world/eslint.config.mjs +2 -27
- package/templates/hello_world/package.json +47 -34
- package/templates/hello_world/scripts/copy_env.ts +10 -0
- package/templates/hello_world/scripts/start/app_runner.ts +39 -2
- package/templates/hello_world/scripts/start/context.ts +12 -6
- package/templates/hello_world/scripts/start/start.ts +11 -0
- package/templates/hello_world/scripts/start/tests/start.tests.ts +61 -0
- package/templates/hello_world/src/app.tsx +20 -0
- package/templates/hello_world/src/tests/__snapshots__/app.tests.tsx.snap +45 -0
- package/templates/hello_world/src/tests/app.tests.tsx +86 -0
- package/templates/hello_world/webpack.config.ts +270 -0
- package/templates/common/conf/eslint-general.mjs +0 -277
- package/templates/common/conf/eslint-i18n.mjs +0 -23
- package/templates/gen_ai/backend/routers/oauth.ts +0 -393
- package/templates/gen_ai/src/components/logged_in_status.tsx +0 -44
- package/templates/gen_ai/src/services/auth.tsx +0 -26
- package/templates/gen_ai/src/services/index.ts +0 -1
|
@@ -1,30 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import type { Configuration } from "webpack";
|
|
2
|
+
import { DefinePlugin, optimize } from "webpack";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import * as TerserPlugin from "terser-webpack-plugin";
|
|
5
|
+
import { transform } from "@formatjs/ts-transformer";
|
|
6
|
+
import * as chalk from "chalk";
|
|
7
|
+
import { config } from "dotenv";
|
|
8
|
+
import { Configuration as DevServerConfiguration } from "webpack-dev-server";
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
* @returns {Object}
|
|
22
|
-
*/
|
|
23
|
-
function buildConfig({
|
|
10
|
+
config();
|
|
11
|
+
|
|
12
|
+
type DevConfig = {
|
|
13
|
+
port: number;
|
|
14
|
+
enableHmr: boolean;
|
|
15
|
+
enableHttps: boolean;
|
|
16
|
+
appOrigin?: string;
|
|
17
|
+
appId?: string; // Deprecated in favour of appOrigin
|
|
18
|
+
certFile?: string;
|
|
19
|
+
keyFile?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export function buildConfig({
|
|
24
23
|
devConfig,
|
|
25
24
|
appEntry = path.join(process.cwd(), "src", "index.tsx"),
|
|
26
25
|
backendHost = process.env.CANVA_BACKEND_HOST,
|
|
27
|
-
|
|
26
|
+
// For IN_HARNESS, refer to the following docs for more information: https://www.canva.dev/docs/apps/test-harness/
|
|
27
|
+
inHarness = process.env.IN_HARNESS === "true",
|
|
28
|
+
}: {
|
|
29
|
+
devConfig?: DevConfig;
|
|
30
|
+
appEntry?: string;
|
|
31
|
+
backendHost?: string;
|
|
32
|
+
inHarness?: boolean;
|
|
33
|
+
} = {}): Configuration & DevServerConfiguration {
|
|
28
34
|
const mode = devConfig ? "development" : "production";
|
|
29
35
|
|
|
30
36
|
if (!backendHost) {
|
|
@@ -45,9 +51,14 @@ function buildConfig({
|
|
|
45
51
|
return {
|
|
46
52
|
mode,
|
|
47
53
|
context: path.resolve(process.cwd(), "./"),
|
|
48
|
-
entry:
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
entry: inHarness
|
|
55
|
+
? {
|
|
56
|
+
harness: path.join(process.cwd(), "harness", "harness.tsx"),
|
|
57
|
+
init: path.join(process.cwd(), "harness", "init.ts"),
|
|
58
|
+
}
|
|
59
|
+
: {
|
|
60
|
+
app: appEntry,
|
|
61
|
+
},
|
|
51
62
|
target: "web",
|
|
52
63
|
resolve: {
|
|
53
64
|
alias: {
|
|
@@ -59,7 +70,7 @@ function buildConfig({
|
|
|
59
70
|
extensions: [".ts", ".tsx", ".js", ".css", ".svg", ".woff", ".woff2"],
|
|
60
71
|
},
|
|
61
72
|
infrastructureLogging: {
|
|
62
|
-
level: "none",
|
|
73
|
+
level: inHarness ? "info" : "none",
|
|
63
74
|
},
|
|
64
75
|
module: {
|
|
65
76
|
rules: [
|
|
@@ -173,32 +184,23 @@ function buildConfig({
|
|
|
173
184
|
}),
|
|
174
185
|
// Apps can only submit a single JS file via the developer portal
|
|
175
186
|
new optimize.LimitChunkCountPlugin({ maxChunks: 1 }),
|
|
176
|
-
],
|
|
187
|
+
].filter(Boolean),
|
|
177
188
|
...buildDevConfig(devConfig),
|
|
178
189
|
};
|
|
179
190
|
}
|
|
180
191
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
* @param {boolean} [options.enableHmr]
|
|
186
|
-
* @param {boolean} [options.enableHttps]
|
|
187
|
-
* @param {string} [options.appOrigin]
|
|
188
|
-
* @param {string} [options.appId] - Deprecated in favour of appOrigin
|
|
189
|
-
* @param {string} [options.certFile]
|
|
190
|
-
* @param {string} [options.keyFile]
|
|
191
|
-
* @returns {Object|null}
|
|
192
|
-
*/
|
|
193
|
-
function buildDevConfig(options) {
|
|
192
|
+
function buildDevConfig(options?: DevConfig): {
|
|
193
|
+
devtool?: string;
|
|
194
|
+
devServer?: DevServerConfiguration;
|
|
195
|
+
} {
|
|
194
196
|
if (!options) {
|
|
195
|
-
return
|
|
197
|
+
return {};
|
|
196
198
|
}
|
|
197
199
|
|
|
198
200
|
const { port, enableHmr, appOrigin, appId, enableHttps, certFile, keyFile } =
|
|
199
201
|
options;
|
|
200
202
|
|
|
201
|
-
let devServer = {
|
|
203
|
+
let devServer: DevServerConfiguration = {
|
|
202
204
|
server: enableHttps
|
|
203
205
|
? {
|
|
204
206
|
type: "https",
|
|
@@ -265,6 +267,4 @@ function buildDevConfig(options) {
|
|
|
265
267
|
};
|
|
266
268
|
}
|
|
267
269
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
module.exports.buildConfig = buildConfig;
|
|
270
|
+
export default buildConfig;
|
|
@@ -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,20 +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
|
-
...i18n,
|
|
13
|
+
...canvaPlugin.configs.apps,
|
|
39
14
|
];
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
"name": "empty-template",
|
|
4
4
|
"description": "An empty Canva App",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"extract": "formatjs extract
|
|
7
|
-
"build": "webpack --config webpack.config.
|
|
6
|
+
"extract": "formatjs extract \"src/**/*.{ts,tsx}\" --out-file dist/messages_en.json",
|
|
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,64 +12,77 @@
|
|
|
12
12
|
"lint:fix": "eslint . --fix",
|
|
13
13
|
"lint:types": "tsc",
|
|
14
14
|
"start": "ts-node ./scripts/start/start.ts",
|
|
15
|
-
"
|
|
16
|
-
"test
|
|
15
|
+
"start:preview": "npm run start -- --preview",
|
|
16
|
+
"test": "jest --no-cache",
|
|
17
|
+
"test:watch": "jest --watchAll",
|
|
18
|
+
"test:update": "npm run test -- -u",
|
|
19
|
+
"postinstall": "ts-node ./scripts/copy_env.ts"
|
|
17
20
|
},
|
|
18
21
|
"dependencies": {
|
|
19
|
-
"@canva/app-
|
|
20
|
-
"@canva/app-
|
|
21
|
-
"@canva/
|
|
22
|
-
"@canva/
|
|
23
|
-
"@canva/error": "^2.
|
|
22
|
+
"@canva/app-i18n-kit": "^1.0.2",
|
|
23
|
+
"@canva/app-ui-kit": "^4.9.0",
|
|
24
|
+
"@canva/asset": "^2.2.0",
|
|
25
|
+
"@canva/design": "^2.4.1",
|
|
26
|
+
"@canva/error": "^2.1.0",
|
|
27
|
+
"@canva/platform": "^2.1.0",
|
|
28
|
+
"@canva/user": "^2.1.0",
|
|
24
29
|
"react": "18.3.1",
|
|
25
30
|
"react-dom": "18.3.1",
|
|
26
|
-
"react-intl": "6.
|
|
31
|
+
"react-intl": "6.8.7"
|
|
27
32
|
},
|
|
28
33
|
"devDependencies": {
|
|
29
|
-
"@eslint
|
|
30
|
-
"@
|
|
31
|
-
"@formatjs/cli": "6.
|
|
32
|
-
"@formatjs/ts-transformer": "3.13.
|
|
34
|
+
"@canva/app-eslint-plugin": "^1.0.0-beta.3",
|
|
35
|
+
"@canva/cli": ">= 0.0.1-beta.13 < 0.0.2",
|
|
36
|
+
"@formatjs/cli": "6.3.15",
|
|
37
|
+
"@formatjs/ts-transformer": "3.13.27",
|
|
33
38
|
"@ngrok/ngrok": "1.4.1",
|
|
39
|
+
"@pmmmwh/react-refresh-webpack-plugin": "0.5.15",
|
|
34
40
|
"@svgr/webpack": "8.1.0",
|
|
35
|
-
"@
|
|
41
|
+
"@testing-library/react": "16.1.0",
|
|
42
|
+
"@types/express": "4.17.21",
|
|
43
|
+
"@types/express-serve-static-core": "4.19.6",
|
|
44
|
+
"@types/jest": "29.5.14",
|
|
45
|
+
"@types/jsonwebtoken": "9.0.7",
|
|
36
46
|
"@types/node": "20.10.0",
|
|
37
|
-
"@types/node-fetch": "2.6.
|
|
47
|
+
"@types/node-fetch": "2.6.12",
|
|
38
48
|
"@types/node-forge": "1.3.11",
|
|
39
49
|
"@types/nodemon": "1.19.6",
|
|
40
|
-
"@types/react": "18.3.
|
|
41
|
-
"@types/react-dom": "18.3.
|
|
50
|
+
"@types/react": "18.3.12",
|
|
51
|
+
"@types/react-dom": "18.3.1",
|
|
42
52
|
"@types/webpack-env": "1.18.5",
|
|
43
|
-
"@typescript-eslint/eslint-plugin": "8.2.0",
|
|
44
|
-
"@typescript-eslint/parser": "8.2.0",
|
|
45
53
|
"chalk": "4.1.2",
|
|
46
54
|
"cli-table3": "0.6.5",
|
|
47
55
|
"css-loader": "7.1.2",
|
|
48
56
|
"css-modules-typescript-loader": "4.0.1",
|
|
49
|
-
"cssnano": "7.0.
|
|
50
|
-
"debug": "4.
|
|
51
|
-
"dotenv": "16.4.
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"eslint-plugin-jest": "28.8.0",
|
|
55
|
-
"eslint-plugin-react": "7.35.0",
|
|
57
|
+
"cssnano": "7.0.6",
|
|
58
|
+
"debug": "4.4.0",
|
|
59
|
+
"dotenv": "16.4.7",
|
|
60
|
+
"express": "4.21.2",
|
|
61
|
+
"express-basic-auth": "1.2.1",
|
|
56
62
|
"jest": "29.7.0",
|
|
57
|
-
"
|
|
63
|
+
"jest-css-modules-transform": "4.4.2",
|
|
64
|
+
"jest-environment-jsdom": "29.7.0",
|
|
65
|
+
"jsonwebtoken": "9.0.2",
|
|
66
|
+
"jwks-rsa": "3.1.0",
|
|
67
|
+
"mini-css-extract-plugin": "2.9.2",
|
|
58
68
|
"node-fetch": "3.3.2",
|
|
59
69
|
"node-forge": "1.3.1",
|
|
60
70
|
"nodemon": "3.0.1",
|
|
71
|
+
"open": "8.4.2",
|
|
61
72
|
"postcss-loader": "8.1.1",
|
|
62
|
-
"prettier": "3.
|
|
73
|
+
"prettier": "3.4.2",
|
|
74
|
+
"react-refresh": "0.16.0",
|
|
63
75
|
"style-loader": "4.0.0",
|
|
64
|
-
"terser-webpack-plugin": "5.3.
|
|
65
|
-
"
|
|
66
|
-
"ts-
|
|
76
|
+
"terser-webpack-plugin": "5.3.11",
|
|
77
|
+
"tree-kill": "1.2.2",
|
|
78
|
+
"ts-jest": "29.2.5",
|
|
79
|
+
"ts-loader": "9.5.2",
|
|
67
80
|
"ts-node": "10.9.2",
|
|
68
81
|
"typescript": "5.5.4",
|
|
69
82
|
"url-loader": "4.1.1",
|
|
70
|
-
"webpack": "5.
|
|
83
|
+
"webpack": "5.97.1",
|
|
71
84
|
"webpack-cli": "5.1.4",
|
|
72
|
-
"webpack-dev-server": "5.0
|
|
85
|
+
"webpack-dev-server": "5.2.0",
|
|
73
86
|
"yargs": "17.7.2"
|
|
74
87
|
}
|
|
75
88
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
|
|
5
|
+
const envPath = path.resolve(__dirname, "..", ".env");
|
|
6
|
+
const templatePath = path.resolve(__dirname, "..", ".env.template");
|
|
7
|
+
|
|
8
|
+
if (!fs.existsSync(envPath)) {
|
|
9
|
+
fs.copyFileSync(templatePath, envPath);
|
|
10
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
import type { Context } from "./context";
|
|
3
3
|
import * as chalk from "chalk";
|
|
4
|
-
import { buildConfig } from "../../webpack.config
|
|
4
|
+
import { buildConfig } from "../../webpack.config";
|
|
5
5
|
import * as ngrok from "@ngrok/ngrok";
|
|
6
6
|
import * as nodemon from "nodemon";
|
|
7
7
|
import * as Table from "cli-table3";
|
|
8
8
|
import * as webpack from "webpack";
|
|
9
9
|
import * as WebpackDevServer from "webpack-dev-server";
|
|
10
|
+
import * as open from "open";
|
|
11
|
+
import { generatePreviewUrl } from "@canva/cli";
|
|
10
12
|
import type { Certificate } from "../ssl/ssl";
|
|
11
13
|
import { createOrRetrieveCertificate } from "../ssl/ssl";
|
|
12
14
|
|
|
@@ -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
|
}
|
|
@@ -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,8 +1,11 @@
|
|
|
1
1
|
import { Button, Rows, Text } from "@canva/app-ui-kit";
|
|
2
|
+
import { requestOpenExternalUrl } from "@canva/platform";
|
|
2
3
|
import { FormattedMessage, useIntl } from "react-intl";
|
|
3
4
|
import * as styles from "styles/components.css";
|
|
4
5
|
import { useAddElement } from "utils/use_add_element";
|
|
5
6
|
|
|
7
|
+
export const DOCS_URL = "https://www.canva.dev/docs/apps/";
|
|
8
|
+
|
|
6
9
|
export const App = () => {
|
|
7
10
|
const addElement = useAddElement();
|
|
8
11
|
|
|
@@ -13,6 +16,16 @@ export const App = () => {
|
|
|
13
16
|
});
|
|
14
17
|
};
|
|
15
18
|
|
|
19
|
+
const openExternalUrl = async (url: string) => {
|
|
20
|
+
const response = await requestOpenExternalUrl({
|
|
21
|
+
url,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (response.status === "aborted") {
|
|
25
|
+
// user decided not to navigate to the link
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
16
29
|
const intl = useIntl();
|
|
17
30
|
|
|
18
31
|
return (
|
|
@@ -37,6 +50,13 @@ export const App = () => {
|
|
|
37
50
|
"Button text to do something cool. Creates a new text element when pressed.",
|
|
38
51
|
})}
|
|
39
52
|
</Button>
|
|
53
|
+
<Button variant="secondary" onClick={() => openExternalUrl(DOCS_URL)}>
|
|
54
|
+
{intl.formatMessage({
|
|
55
|
+
defaultMessage: "Open Canva Apps SDK docs",
|
|
56
|
+
description:
|
|
57
|
+
"Button text to open Canva Apps SDK docs. Opens an external URL when pressed.",
|
|
58
|
+
})}
|
|
59
|
+
</Button>
|
|
40
60
|
</Rows>
|
|
41
61
|
</div>
|
|
42
62
|
);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`Hello World Tests should have a consistent snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="scrollContainer"
|
|
7
|
+
>
|
|
8
|
+
<div
|
|
9
|
+
class="jv_R6g"
|
|
10
|
+
style="--NZu1Zw: 16px;"
|
|
11
|
+
>
|
|
12
|
+
<p
|
|
13
|
+
class="aWBg0w aZskFA u16U_g"
|
|
14
|
+
>
|
|
15
|
+
To make changes to this app, edit the
|
|
16
|
+
<code>
|
|
17
|
+
src/app.tsx
|
|
18
|
+
</code>
|
|
19
|
+
file, then close and reopen the app in the editor to preview the changes.
|
|
20
|
+
</p>
|
|
21
|
+
<button
|
|
22
|
+
class="_5KtATA LQzFZw VgvqkQ _8ERLTg rV61VQ LQzFZw VgvqkQ _4_iekA _3oHQrw j34Zww aqbYPg _3FvZZg"
|
|
23
|
+
type="button"
|
|
24
|
+
>
|
|
25
|
+
<span
|
|
26
|
+
class="khPe7Q"
|
|
27
|
+
>
|
|
28
|
+
Do something cool
|
|
29
|
+
</span>
|
|
30
|
+
</button>
|
|
31
|
+
<button
|
|
32
|
+
class="_5KtATA LQzFZw VgvqkQ _8ERLTg Z3nT2A LQzFZw VgvqkQ _4_iekA j34Zww aqbYPg _3FvZZg"
|
|
33
|
+
type="button"
|
|
34
|
+
>
|
|
35
|
+
<span
|
|
36
|
+
class="khPe7Q"
|
|
37
|
+
>
|
|
38
|
+
Open Canva Apps SDK docs
|
|
39
|
+
</span>
|
|
40
|
+
</button>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
,
|
|
44
|
+
</div>
|
|
45
|
+
`;
|