From 2083af63395295fcf0fddef4f6537e9d5ecfca5f Mon Sep 17 00:00:00 2001 From: ning Date: Wed, 20 Jun 2018 20:05:26 +0800 Subject: [PATCH 1/9] Add terraform and deployment scripts --- .gitignore | 3 +++ package.json | 2 +- terraform/main.tf | 36 ++++++++++++++++++++++++++++++++++++ terraform/sync-build.sh | 3 +++ tsconfig.prod.json | 6 ++++++ 5 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 terraform/main.tf create mode 100755 terraform/sync-build.sh create mode 100644 tsconfig.prod.json diff --git a/.gitignore b/.gitignore index 5cbcdd3336..7759308e14 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,9 @@ /build src/styles/*.css +# deployment +terraform* + # misc .DS_Store .env.local diff --git a/package.json b/package.json index 02548eebed..feda638acc 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "update-ui-snapshots": "Update UI test snapshots" }, "scripts": { - "build": "build-css && build-js", + "build": "node-sass-chokidar src/ -o src/ && react-scripts-ts build", "build-css": "node-sass-chokidar src/ -o src/", "build-js": "react-scripts-ts build", "coverage": "./scripts/coverage-fix.sh do && react-scripts-ts test --env=jsdom --coverage && ./scripts/coverage-fix.sh undo", diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000000..ee856868fc --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,36 @@ +variable "bucket_name" { + default = "cadet-frontend-stg" +} + +provider "aws" { + region = "ap-southeast-1" +} + +resource "aws_s3_bucket" "stg-site" { + bucket = "${var.bucket_name}" + force_destroy = true + website { + index_document = "index.html" + error_document = "index.html" + } + policy = < Date: Wed, 20 Jun 2018 21:27:16 +0800 Subject: [PATCH 2/9] Hotfix errors due to minifying js assets 1. It seems that using shorthand generator function definitions in objects (for src/slang/interpreter.ts) will result in a minified file where the generator method replaced by a function. `yield` calls in this generator therefore raise an uncaught syntax error in the browser. See: https://github.com/webpack/webpack/issues/7566 2. A known issue with causes reuse of const variables in the same scope. See: https://github.com/mishoo/UglifyJS2/issues/2842 --- package.json | 4 +- src/slang/interpreter.ts | 13 +- src/utils/registerServiceWorker.ts | 6 +- yarn.lock | 225 ++++++++++++++++++++--------- 4 files changed, 170 insertions(+), 78 deletions(-) diff --git a/package.json b/package.json index feda638acc..d210d1d731 100644 --- a/package.json +++ b/package.json @@ -83,12 +83,14 @@ "@types/react-router-redux": "^5.0.13", "@types/react-test-renderer": "^16.0.1", "@types/redux-mock-store": "^0.0.13", + "babel-core": "6", + "babel-runtime": "^6.23.0", "coveralls": "^3.0.1", "enzyme": "^3.3.0", "enzyme-adapter-react-16": "^1.1.1", "husky": "^1.0.0-rc.6", "prettier": "^1.12.0", - "react-scripts-ts": "^2.15.1", + "react-scripts-ts": "^2.16.0", "react-test-renderer": "^16.3.1", "typescript": "^2.8.1" } diff --git a/src/slang/interpreter.ts b/src/slang/interpreter.ts index c17a3a0efb..db7922e09b 100644 --- a/src/slang/interpreter.ts +++ b/src/slang/interpreter.ts @@ -366,9 +366,7 @@ export const evaluators: { [nodeType: string]: Evaluator } = { return undefined } }, - *ExpressionStatement(node: es.ExpressionStatement, context: Context) { - return yield* evaluate(node.expression, context) - }, + ExpressionStatement: expressionStatementEvaluator, *ReturnStatement(node: es.ReturnStatement, context: Context) { if (node.argument) { if (node.argument.type === 'CallExpression') { @@ -438,6 +436,15 @@ export const evaluators: { [nodeType: string]: Evaluator } = { } } +/** + * This is used as part of the evaluators object in this file, but must be + * defined outside, otherwise webpack parses it wrongly, resulting in a fatal + * syntax error: https://github.com/webpack/webpack/issues/7566 + */ +function* expressionStatementEvaluator(node: es.ExpressionStatement, context: Context) { + return yield* evaluate(node.expression, context) +} + export function* evaluate(node: es.Node, context: Context) { yield* visit(context, node) const result = yield* evaluators[node.type](node, context) diff --git a/src/utils/registerServiceWorker.ts b/src/utils/registerServiceWorker.ts index 1c15ee1ee0..3d552f92ec 100644 --- a/src/utils/registerServiceWorker.ts +++ b/src/utils/registerServiceWorker.ts @@ -29,11 +29,9 @@ export default function register() { } window.addEventListener('load', () => { - const swUrl = `${process.env.PUBLIC_URL}/service-worker.js` - if (isLocalhost) { // This is running on localhost. Lets check if a service worker still exists or not. - checkValidServiceWorker(swUrl) + checkValidServiceWorker(`${process.env.PUBLIC_URL}/service-worker.js`) // Add some additional logging to localhost, pointing developers to the // service worker/PWA documentation. @@ -45,7 +43,7 @@ export default function register() { }) } else { // Is not local host. Just register service worker - registerValidSW(swUrl) + registerValidSW(`${process.env.PUBLIC_URL}/service-worker.js`) } }) } diff --git a/yarn.lock b/yarn.lock index 0fbd9acf2e..e44b6e2392 100644 --- a/yarn.lock +++ b/yarn.lock @@ -680,6 +680,30 @@ babel-code-frame@6.26.0, babel-code-frame@^6.11.0, babel-code-frame@^6.22.0, bab esutils "^2.0.2" js-tokens "^3.0.2" +babel-core@6: + version "6.26.3" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" + dependencies: + babel-code-frame "^6.26.0" + babel-generator "^6.26.0" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + convert-source-map "^1.5.1" + debug "^2.6.9" + json5 "^0.5.1" + lodash "^4.17.4" + minimatch "^3.0.4" + path-is-absolute "^1.0.1" + private "^0.1.8" + slash "^1.0.0" + source-map "^0.5.7" + babel-core@^6.0.0, babel-core@^6.24.1, babel-core@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.0.tgz#af32f78b31a6fcef119c87b0fd8d9753f03a0bb8" @@ -826,13 +850,20 @@ babel-helpers@^6.24.1: babel-runtime "^6.22.0" babel-template "^6.24.1" -babel-jest@^22.1.0, babel-jest@^22.4.3: +babel-jest@^22.1.0: version "22.4.3" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-22.4.3.tgz#4b7a0b6041691bbd422ab49b3b73654a49a6627a" dependencies: babel-plugin-istanbul "^4.1.5" babel-preset-jest "^22.4.3" +babel-jest@^22.4.4: + version "22.4.4" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-22.4.4.tgz#977259240420e227444ebe49e226a61e49ea659d" + dependencies: + babel-plugin-istanbul "^4.1.5" + babel-preset-jest "^22.4.4" + babel-loader@^7.1.2: version "7.1.4" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.4.tgz#e3463938bd4e6d55d1c174c5485d406a188ed015" @@ -874,6 +905,10 @@ babel-plugin-jest-hoist@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.4.3.tgz#7d8bcccadc2667f96a0dcc6afe1891875ee6c14a" +babel-plugin-jest-hoist@^22.4.4: + version "22.4.4" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.4.4.tgz#b9851906eab34c7bf6f8c895a2b08bea1a844c0b" + babel-plugin-syntax-async-functions@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" @@ -1214,6 +1249,13 @@ babel-preset-jest@^22.0.1, babel-preset-jest@^22.4.3: babel-plugin-jest-hoist "^22.4.3" babel-plugin-syntax-object-rest-spread "^6.13.0" +babel-preset-jest@^22.4.4: + version "22.4.4" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-22.4.4.tgz#ec9fbd8bcd7dfd24b8b5320e0e688013235b7c39" + dependencies: + babel-plugin-jest-hoist "^22.4.4" + babel-plugin-syntax-object-rest-spread "^6.13.0" + babel-preset-react-app@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/babel-preset-react-app/-/babel-preset-react-app-3.1.1.tgz#d3f06a79742f0e89d7afcb72e282d9809c850920" @@ -1255,7 +1297,7 @@ babel-register@^6.26.0: mkdirp "^0.5.1" source-map-support "^0.4.15" -babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0, babel-runtime@^6.9.2: +babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0, babel-runtime@^6.9.2: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" dependencies: @@ -1994,7 +2036,7 @@ content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" -convert-source-map@^1.4.0, convert-source-map@^1.5.0: +convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" @@ -2317,7 +2359,7 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -2966,7 +3008,7 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -expect@^22.4.3: +expect@^22.4.0, expect@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/expect/-/expect-22.4.3.tgz#d5a29d0a0e1fb2153557caef2674d4547e914674" dependencies: @@ -4438,15 +4480,15 @@ istanbul-reports@^1.3.0: dependencies: handlebars "^4.0.3" -jest-changed-files@^22.4.3: +jest-changed-files@^22.2.0: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-22.4.3.tgz#8882181e022c38bd46a2e4d18d44d19d90a90fb2" dependencies: throat "^4.0.0" -jest-cli@^22.1.4: - version "22.4.3" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-22.4.3.tgz#bf16c4a5fb7edc3fa5b9bb7819e34139e88a72c7" +jest-cli@^22.4.2: + version "22.4.4" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-22.4.4.tgz#68cd2a2aae983adb1e6638248ca21082fd6d9e90" dependencies: ansi-escapes "^3.0.0" chalk "^2.0.1" @@ -4459,20 +4501,20 @@ jest-cli@^22.1.4: istanbul-lib-coverage "^1.1.1" istanbul-lib-instrument "^1.8.0" istanbul-lib-source-maps "^1.2.1" - jest-changed-files "^22.4.3" - jest-config "^22.4.3" - jest-environment-jsdom "^22.4.3" - jest-get-type "^22.4.3" - jest-haste-map "^22.4.3" - jest-message-util "^22.4.3" - jest-regex-util "^22.4.3" - jest-resolve-dependencies "^22.4.3" - jest-runner "^22.4.3" - jest-runtime "^22.4.3" - jest-snapshot "^22.4.3" - jest-util "^22.4.3" - jest-validate "^22.4.3" - jest-worker "^22.4.3" + jest-changed-files "^22.2.0" + jest-config "^22.4.4" + jest-environment-jsdom "^22.4.1" + jest-get-type "^22.1.0" + jest-haste-map "^22.4.2" + jest-message-util "^22.4.0" + jest-regex-util "^22.1.0" + jest-resolve-dependencies "^22.1.0" + jest-runner "^22.4.4" + jest-runtime "^22.4.4" + jest-snapshot "^22.4.0" + jest-util "^22.4.1" + jest-validate "^22.4.4" + jest-worker "^22.2.2" micromatch "^2.3.11" node-notifier "^5.2.1" realpath-native "^1.0.0" @@ -4499,7 +4541,23 @@ jest-config@^22.0.1, jest-config@^22.4.3: jest-validate "^22.4.3" pretty-format "^22.4.3" -jest-diff@^22.4.3: +jest-config@^22.4.4: + version "22.4.4" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-22.4.4.tgz#72a521188720597169cd8b4ff86934ef5752d86a" + dependencies: + chalk "^2.0.1" + glob "^7.1.1" + jest-environment-jsdom "^22.4.1" + jest-environment-node "^22.4.1" + jest-get-type "^22.1.0" + jest-jasmine2 "^22.4.4" + jest-regex-util "^22.1.0" + jest-resolve "^22.4.2" + jest-util "^22.4.1" + jest-validate "^22.4.4" + pretty-format "^22.4.0" + +jest-diff@^22.4.0, jest-diff@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-22.4.3.tgz#e18cc3feff0aeef159d02310f2686d4065378030" dependencies: @@ -4508,13 +4566,13 @@ jest-diff@^22.4.3: jest-get-type "^22.4.3" pretty-format "^22.4.3" -jest-docblock@^22.4.3: +jest-docblock@^22.4.0, jest-docblock@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-22.4.3.tgz#50886f132b42b280c903c592373bb6e93bb68b19" dependencies: detect-newline "^2.1.0" -jest-environment-jsdom@^22.4.3: +jest-environment-jsdom@^22.4.1, jest-environment-jsdom@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-22.4.3.tgz#d67daa4155e33516aecdd35afd82d4abf0fa8a1e" dependencies: @@ -4522,18 +4580,18 @@ jest-environment-jsdom@^22.4.3: jest-util "^22.4.3" jsdom "^11.5.1" -jest-environment-node@^22.4.3: +jest-environment-node@^22.4.1, jest-environment-node@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-22.4.3.tgz#54c4eaa374c83dd52a9da8759be14ebe1d0b9129" dependencies: jest-mock "^22.4.3" jest-util "^22.4.3" -jest-get-type@^22.4.3: +jest-get-type@^22.1.0, jest-get-type@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4" -jest-haste-map@^22.4.3: +jest-haste-map@^22.4.2: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-22.4.3.tgz#25842fa2ba350200767ac27f658d58b9d5c2e20b" dependencies: @@ -4561,13 +4619,29 @@ jest-jasmine2@^22.4.3: jest-util "^22.4.3" source-map-support "^0.5.0" -jest-leak-detector@^22.4.3: +jest-jasmine2@^22.4.4: + version "22.4.4" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-22.4.4.tgz#c55f92c961a141f693f869f5f081a79a10d24e23" + dependencies: + chalk "^2.0.1" + co "^4.6.0" + expect "^22.4.0" + graceful-fs "^4.1.11" + is-generator-fn "^1.0.0" + jest-diff "^22.4.0" + jest-matcher-utils "^22.4.0" + jest-message-util "^22.4.0" + jest-snapshot "^22.4.0" + jest-util "^22.4.1" + source-map-support "^0.5.0" + +jest-leak-detector@^22.4.0: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-22.4.3.tgz#2b7b263103afae8c52b6b91241a2de40117e5b35" dependencies: pretty-format "^22.4.3" -jest-matcher-utils@^22.4.3: +jest-matcher-utils@^22.4.0, jest-matcher-utils@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz#4632fe428ebc73ebc194d3c7b65d37b161f710ff" dependencies: @@ -4575,7 +4649,7 @@ jest-matcher-utils@^22.4.3: jest-get-type "^22.4.3" pretty-format "^22.4.3" -jest-message-util@^22.4.3: +jest-message-util@^22.4.0, jest-message-util@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-22.4.3.tgz#cf3d38aafe4befddbfc455e57d65d5239e399eb7" dependencies: @@ -4589,56 +4663,56 @@ jest-mock@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-22.4.3.tgz#f63ba2f07a1511772cdc7979733397df770aabc7" -jest-regex-util@^22.4.3: +jest-regex-util@^22.1.0, jest-regex-util@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-22.4.3.tgz#a826eb191cdf22502198c5401a1fc04de9cef5af" -jest-resolve-dependencies@^22.4.3: +jest-resolve-dependencies@^22.1.0: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-22.4.3.tgz#e2256a5a846732dc3969cb72f3c9ad7725a8195e" dependencies: jest-regex-util "^22.4.3" -jest-resolve@^22.4.3: +jest-resolve@^22.4.2, jest-resolve@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-22.4.3.tgz#0ce9d438c8438229aa9b916968ec6b05c1abb4ea" dependencies: browser-resolve "^1.11.2" chalk "^2.0.1" -jest-runner@^22.4.3: - version "22.4.3" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-22.4.3.tgz#298ddd6a22b992c64401b4667702b325e50610c3" +jest-runner@^22.4.4: + version "22.4.4" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-22.4.4.tgz#dfca7b7553e0fa617e7b1291aeb7ce83e540a907" dependencies: exit "^0.1.2" - jest-config "^22.4.3" - jest-docblock "^22.4.3" - jest-haste-map "^22.4.3" - jest-jasmine2 "^22.4.3" - jest-leak-detector "^22.4.3" - jest-message-util "^22.4.3" - jest-runtime "^22.4.3" - jest-util "^22.4.3" - jest-worker "^22.4.3" + jest-config "^22.4.4" + jest-docblock "^22.4.0" + jest-haste-map "^22.4.2" + jest-jasmine2 "^22.4.4" + jest-leak-detector "^22.4.0" + jest-message-util "^22.4.0" + jest-runtime "^22.4.4" + jest-util "^22.4.1" + jest-worker "^22.2.2" throat "^4.0.0" -jest-runtime@^22.4.3: - version "22.4.3" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-22.4.3.tgz#b69926c34b851b920f666c93e86ba2912087e3d0" +jest-runtime@^22.4.4: + version "22.4.4" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-22.4.4.tgz#9ba7792fc75582a5be0f79af6f8fe8adea314048" dependencies: babel-core "^6.0.0" - babel-jest "^22.4.3" + babel-jest "^22.4.4" babel-plugin-istanbul "^4.1.5" chalk "^2.0.1" convert-source-map "^1.4.0" exit "^0.1.2" graceful-fs "^4.1.11" - jest-config "^22.4.3" - jest-haste-map "^22.4.3" - jest-regex-util "^22.4.3" - jest-resolve "^22.4.3" - jest-util "^22.4.3" - jest-validate "^22.4.3" + jest-config "^22.4.4" + jest-haste-map "^22.4.2" + jest-regex-util "^22.1.0" + jest-resolve "^22.4.2" + jest-util "^22.4.1" + jest-validate "^22.4.4" json-stable-stringify "^1.0.1" micromatch "^2.3.11" realpath-native "^1.0.0" @@ -4651,7 +4725,7 @@ jest-serializer@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-22.4.3.tgz#a679b81a7f111e4766235f4f0c46d230ee0f7436" -jest-snapshot@^22.4.3: +jest-snapshot@^22.4.0, jest-snapshot@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-22.4.3.tgz#b5c9b42846ffb9faccb76b841315ba67887362d2" dependencies: @@ -4662,7 +4736,7 @@ jest-snapshot@^22.4.3: natural-compare "^1.4.0" pretty-format "^22.4.3" -jest-util@^22.4.3: +jest-util@^22.4.1, jest-util@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-22.4.3.tgz#c70fec8eec487c37b10b0809dc064a7ecf6aafac" dependencies: @@ -4684,17 +4758,28 @@ jest-validate@^22.4.3: leven "^2.1.0" pretty-format "^22.4.3" -jest-worker@^22.4.3: +jest-validate@^22.4.4: + version "22.4.4" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-22.4.4.tgz#1dd0b616ef46c995de61810d85f57119dbbcec4d" + dependencies: + chalk "^2.0.1" + jest-config "^22.4.4" + jest-get-type "^22.1.0" + leven "^2.1.0" + pretty-format "^22.4.0" + +jest-worker@^22.2.2, jest-worker@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-22.4.3.tgz#5c421417cba1c0abf64bf56bd5fb7968d79dd40b" dependencies: merge-stream "^1.0.1" -jest@22.1.4: - version "22.1.4" - resolved "https://registry.yarnpkg.com/jest/-/jest-22.1.4.tgz#9ec71373a38f40ff92a3e5e96ae85687c181bb72" +jest@22.4.2: + version "22.4.2" + resolved "https://registry.yarnpkg.com/jest/-/jest-22.4.2.tgz#34012834a49bf1bdd3bc783850ab44e4499afc20" dependencies: - jest-cli "^22.1.4" + import-local "^1.0.0" + jest-cli "^22.4.2" js-base64@^2.1.8: version "2.4.5" @@ -6432,14 +6517,14 @@ pretty-error@^2.0.2: renderkid "^2.0.1" utila "~0.4" -pretty-format@^22.4.3: +pretty-format@^22.4.0, pretty-format@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-22.4.3.tgz#f873d780839a9c02e9664c8a082e9ee79eaac16f" dependencies: ansi-regex "^3.0.0" ansi-styles "^3.2.0" -private@^0.1.6, private@^0.1.7: +private@^0.1.6, private@^0.1.7, private@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -6775,9 +6860,9 @@ react-router@^4.2.0: prop-types "^15.5.4" warning "^3.0.0" -react-scripts-ts@^2.15.1: - version "2.15.1" - resolved "https://registry.yarnpkg.com/react-scripts-ts/-/react-scripts-ts-2.15.1.tgz#98032840e61faff65e59047036a7fa236568d503" +react-scripts-ts@^2.16.0: + version "2.16.0" + resolved "https://registry.yarnpkg.com/react-scripts-ts/-/react-scripts-ts-2.16.0.tgz#45f831a12139c3b59d6bb729c1b6ef51e0f22908" dependencies: autoprefixer "7.1.6" babel-jest "^22.1.0" @@ -6793,7 +6878,7 @@ react-scripts-ts@^2.15.1: fork-ts-checker-webpack-plugin "^0.2.8" fs-extra "3.0.1" html-webpack-plugin "2.29.0" - jest "22.1.4" + jest "22.4.2" object-assign "4.1.1" postcss-flexbugs-fixes "3.2.0" postcss-loader "2.0.8" From 8c451f3829f058ab020a7fa71ecd69821840f37c Mon Sep 17 00:00:00 2001 From: ning Date: Wed, 20 Jun 2018 22:18:27 +0800 Subject: [PATCH 3/9] Fix workspace-green css --- public/workspace-green.css | 27 +++++++++++ src/components/Playground.tsx | 47 +++++++++++++------ .../__snapshots__/Playground.tsx.snap | 2 +- src/components/workspace/index.tsx | 9 +--- src/styles/workspace-green.scss | 38 --------------- 5 files changed, 63 insertions(+), 60 deletions(-) create mode 100644 public/workspace-green.css delete mode 100644 src/styles/workspace-green.scss diff --git a/public/workspace-green.css b/public/workspace-green.css new file mode 100644 index 0000000000..992f619469 --- /dev/null +++ b/public/workspace-green.css @@ -0,0 +1,27 @@ +.NavigationBar { + display: none !important; } + +.workspace { + background: #00ff00 !important; } + +#brace-editor { + background: #00ff00 !important; } + +.ace_print-margin { + background: #00e000 !important; } + +/* editor specific */ +.editor-react-ace { + border: 0.1rem solid #00e000 !important; } + +.ace_gutter { + background: #00ff00 !important; } + +.ace_gutter-layer { + background: #00ff00 !important; } + +/* repl specific */ +.pt-card { + background: #00ff00 !important; + box-shadow: none !important; + border: 0.1rem solid #00e000 !important; } diff --git a/src/components/Playground.tsx b/src/components/Playground.tsx index a071e9c40c..9317f79976 100644 --- a/src/components/Playground.tsx +++ b/src/components/Playground.tsx @@ -11,16 +11,39 @@ import { SideContentTab } from './workspace/side-content' export type PlaygroundProps = RouteComponentProps<{}> -const Playground: React.SFC = props => { - return ( - - - - ) +type PlaygroundState = { + isGreen: boolean +} + +class Playground extends React.Component { + private keyMap = { goGreen: 'h u l k' } + + private handlers = { goGreen: () => {} } + + constructor(props: PlaygroundProps) { + super(props) + this.state = { isGreen: false } + this.handlers.goGreen = this.toggleIsGreen.bind(this) + } + + public render() { + return ( + + {this.state.isGreen ? ( + + ) : null} + + + ) + } + + private toggleIsGreen() { + this.setState({ isGreen: !this.state.isGreen }) + } } const parsePrgrm = (props: PlaygroundProps) => { @@ -35,10 +58,6 @@ const parseLibrary = (props: PlaygroundProps) => { return sourceChapters.includes(lib) ? lib : undefined } -const keyMap = { - goGreen: 'h u l k' -} - const SICP_SITE = 'http://www.comp.nus.edu.sg/~henz/sicp_js/' const CHAP = '\xa7' const playgroundIntroduction: SideContentTab = { diff --git a/src/components/__tests__/__snapshots__/Playground.tsx.snap b/src/components/__tests__/__snapshots__/Playground.tsx.snap index a5b4bf57c4..3d166f7d57 100644 --- a/src/components/__tests__/__snapshots__/Playground.tsx.snap +++ b/src/components/__tests__/__snapshots__/Playground.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Playground renders correctly 1`] = ` -" +" " `; diff --git a/src/components/workspace/index.tsx b/src/components/workspace/index.tsx index cdcaad1738..83495aaeef 100644 --- a/src/components/workspace/index.tsx +++ b/src/components/workspace/index.tsx @@ -1,7 +1,6 @@ import { decompressFromEncodedURIComponent } from 'lz-string' import Resizable, { ResizableProps, ResizeCallback } from 're-resizable' import * as React from 'react' -import { HotKeys } from 'react-hotkeys' import ControlBarContainer from '../../containers/workspace/ControlBarContainer' import EditorContainer from '../../containers/workspace/EditorContainer' @@ -60,7 +59,7 @@ class Workspace extends React.Component { */ public render() { return ( - +
(this.editorDividerDiv = e!)} /> @@ -75,7 +74,7 @@ class Workspace extends React.Component {
- +
) } @@ -154,10 +153,6 @@ class Workspace extends React.Component { } } -const handlers = { - goGreen: () => require('../../styles/workspace-green.css') -} - const rightResizeOnly = { top: false, right: true, diff --git a/src/styles/workspace-green.scss b/src/styles/workspace-green.scss deleted file mode 100644 index 74acded089..0000000000 --- a/src/styles/workspace-green.scss +++ /dev/null @@ -1,38 +0,0 @@ -$pure-green: #00ff00; -$dark-green: #00e000; - -.NavigationBar { - display: none !important; -} - -.Playground { - background: $pure-green !important; -} - -#brace-editor { - background: $pure-green !important; -} - -.ace_print-margin { - background: $dark-green !important; -} - -/* editor specific */ -.editor-react-ace { - border: 0.1rem solid $dark-green !important; -} - -.ace_gutter { - background: $pure-green !important; -} - -.ace_gutter-layer { - background: $pure-green !important; -} - -/* repl specific */ -.pt-card { - background: $pure-green !important; - box-shadow: none !important; - border: 0.1rem solid $dark-green !important; -} From a7dab582d003a8ad7492d6773a28bb261f30369a Mon Sep 17 00:00:00 2001 From: ning Date: Wed, 20 Jun 2018 23:09:52 +0800 Subject: [PATCH 4/9] Add profile variable to terraform config --- terraform/main.tf | 6 ++++-- terraform/sync-build.sh | 9 ++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/terraform/main.tf b/terraform/main.tf index ee856868fc..6536472ce6 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -1,9 +1,11 @@ variable "bucket_name" { - default = "cadet-frontend-stg" + default = "stg-cadet-frontend" } +variable "credentials-profile" {} provider "aws" { region = "ap-southeast-1" + profile = "${var.credentials-profile}" } resource "aws_s3_bucket" "stg-site" { @@ -31,6 +33,6 @@ resource "aws_s3_bucket" "stg-site" { } EOF provisioner "local-exec" { - command = "yarn build && ./sync-build.sh" + command = "yarn build && ./sync-build.sh ${var.credentials-profile}" } } diff --git a/terraform/sync-build.sh b/terraform/sync-build.sh index d9bb87ae5e..f4127c5683 100755 --- a/terraform/sync-build.sh +++ b/terraform/sync-build.sh @@ -1,3 +1,10 @@ #! /usr/bin/env bash -aws s3 sync ../build s3://cadet-frontend-stg +main() { + if [ ! -z "$1" ]; then + profile="--profile $1" + fi + aws s3 sync ../build s3://stg-cadet-frontend ${profile} +} + +main $1 From 0f629befe8b44fa6bda76b3adafc78aa0aaa2205 Mon Sep 17 00:00:00 2001 From: ning Date: Wed, 20 Jun 2018 23:36:57 +0800 Subject: [PATCH 5/9] Fix runInContext not finishing in minified js --- src/sagas/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sagas/index.ts b/src/sagas/index.ts index 4afee9cf6c..5484140bfc 100644 --- a/src/sagas/index.ts +++ b/src/sagas/index.ts @@ -105,7 +105,7 @@ function* workspaceSaga(): SagaIterator { function* evalCode(code: string, context: Context) { const { result, interrupted } = yield race({ - result: call(runInContext, code, context), + result: call(runInContext, code, context, {scheduler: 'async'}), interrupted: take(actionTypes.INTERRUPT_EXECUTION) }) if (result) { From 274394d512d6c848e5b78d5d3b92506d5e22d469 Mon Sep 17 00:00:00 2001 From: ning Date: Thu, 21 Jun 2018 11:08:52 +0800 Subject: [PATCH 6/9] Fix more instances of method shorthand bugs --- src/sagas/index.ts | 2 +- src/slang/interpreter.ts | 70 +++++++++++++++++++++------------------- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/src/sagas/index.ts b/src/sagas/index.ts index 5484140bfc..c10d7ab110 100644 --- a/src/sagas/index.ts +++ b/src/sagas/index.ts @@ -105,7 +105,7 @@ function* workspaceSaga(): SagaIterator { function* evalCode(code: string, context: Context) { const { result, interrupted } = yield race({ - result: call(runInContext, code, context, {scheduler: 'async'}), + result: call(runInContext, code, context, { scheduler: 'async' }), interrupted: take(actionTypes.INTERRUPT_EXECUTION) }) if (result) { diff --git a/src/slang/interpreter.ts b/src/slang/interpreter.ts index db7922e09b..1fb450064d 100644 --- a/src/slang/interpreter.ts +++ b/src/slang/interpreter.ts @@ -1,4 +1,5 @@ /* tslint:disable: max-classes-per-file */ +/* tslint:disable: object-literal-shorthand*/ import * as es from 'estree' import * as constants from './constants' import { toJS } from './interop' @@ -124,31 +125,41 @@ function* getArgs(context: Context, call: es.CallExpression) { export type Evaluator = (node: T, context: Context) => IterableIterator +/** + * WARNING: Do not use object literal shorthands, e.g. + * { + * *Literal(node: es.Literal, ...) {...}, + * *ThisExpression(node: es.ThisExpression, ..._ {...}, + * ... + * } + * They do not minify well, raising uncaught syntax errors in production. + * See: https://github.com/webpack/webpack/issues/7566 + */ export const evaluators: { [nodeType: string]: Evaluator } = { /** Simple Values */ - *Literal(node: es.Literal, context: Context) { + Literal: function*(node: es.Literal, context: Context) { return node.value }, - *ThisExpression(node: es.ThisExpression, context: Context) { + ThisExpression: function*(node: es.ThisExpression, context: Context) { return context.runtime.frames[0].thisContext }, - *ArrayExpression(node: es.ArrayExpression, context: Context) { + ArrayExpression: function*(node: es.ArrayExpression, context: Context) { const res = [] for (const n of node.elements) { res.push(yield* evaluate(n, context)) } return res }, - *FunctionExpression(node: es.FunctionExpression, context: Context) { + FunctionExpression: function*(node: es.FunctionExpression, context: Context) { return new Closure(node, currentFrame(context), context) }, - *ArrowFunctionExpression(node: es.Function, context: Context) { + ArrowFunctionExpression: function*(node: es.Function, context: Context) { return new ArrowClosure(node, currentFrame(context), context) }, - *Identifier(node: es.Identifier, context: Context) { + Identifier: function*(node: es.Identifier, context: Context) { return getVariable(context, node.name) }, - *CallExpression(node: es.CallExpression, context: Context) { + CallExpression: function*(node: es.CallExpression, context: Context) { const callee = yield* evaluate(node.callee, context) const args = yield* getArgs(context, node) let thisContext @@ -158,7 +169,7 @@ export const evaluators: { [nodeType: string]: Evaluator } = { const result = yield* apply(context, callee, args, node, thisContext) return result }, - *NewExpression(node: es.NewExpression, context: Context) { + NewExpression: function*(node: es.NewExpression, context: Context) { const callee = yield* evaluate(node.callee, context) const args = [] for (const arg of node.arguments) { @@ -174,7 +185,7 @@ export const evaluators: { [nodeType: string]: Evaluator } = { } return obj }, - *UnaryExpression(node: es.UnaryExpression, context: Context) { + UnaryExpression: function*(node: es.UnaryExpression, context: Context) { const value = yield* evaluate(node.argument, context) const error = rttc.checkUnaryExpression(context, node.operator, value) @@ -191,7 +202,7 @@ export const evaluators: { [nodeType: string]: Evaluator } = { return +value } }, - *BinaryExpression(node: es.BinaryExpression, context: Context) { + BinaryExpression: function*(node: es.BinaryExpression, context: Context) { const left = yield* evaluate(node.left, context) const right = yield* evaluate(node.right, context) @@ -241,10 +252,10 @@ export const evaluators: { [nodeType: string]: Evaluator } = { } return result }, - *ConditionalExpression(node: es.ConditionalExpression, context: Context) { + ConditionalExpression: function*(node: es.ConditionalExpression, context: Context) { return yield* this.IfStatement(node, context) }, - *LogicalExpression(node: es.LogicalExpression, context: Context) { + LogicalExpression: function*(node: es.LogicalExpression, context: Context) { const left = yield* evaluate(node.left, context) let error = rttc.checkLogicalExpression(context, left, true) if (error) { @@ -264,20 +275,20 @@ export const evaluators: { [nodeType: string]: Evaluator } = { return left } }, - *VariableDeclaration(node: es.VariableDeclaration, context: Context) { + VariableDeclaration: function*(node: es.VariableDeclaration, context: Context) { const declaration = node.declarations[0] const id = declaration.id as es.Identifier const value = yield* evaluate(declaration.init!, context) defineVariable(context, id.name, value) return undefined }, - *ContinueStatement(node: es.ContinueStatement, context: Context) { + ContinueStatement: function*(node: es.ContinueStatement, context: Context) { return new ContinueValue() }, - *BreakStatement(node: es.BreakStatement, context: Context) { + BreakStatement: function*(node: es.BreakStatement, context: Context) { return new BreakValue() }, - *ForStatement(node: es.ForStatement, context: Context) { + ForStatement: function*(node: es.ForStatement, context: Context) { if (node.init) { yield* evaluate(node.init, context) } @@ -305,7 +316,7 @@ export const evaluators: { [nodeType: string]: Evaluator } = { } return value }, - *MemberExpression(node: es.MemberExpression, context: Context) { + MemberExpression: function*(node: es.MemberExpression, context: Context) { let obj = yield* evaluate(node.object, context) if (obj instanceof Closure) { obj = obj.fun @@ -322,7 +333,7 @@ export const evaluators: { [nodeType: string]: Evaluator } = { } } }, - *AssignmentExpression(node: es.AssignmentExpression, context: Context) { + AssignmentExpression: function*(node: es.AssignmentExpression, context: Context) { if (node.left.type === 'MemberExpression') { const left = node.left const obj = yield* evaluate(left.object, context) @@ -342,7 +353,7 @@ export const evaluators: { [nodeType: string]: Evaluator } = { setVariable(context, id.name, value) return value }, - *FunctionDeclaration(node: es.FunctionDeclaration, context: Context) { + FunctionDeclaration: function*(node: es.FunctionDeclaration, context: Context) { const id = node.id as es.Identifier // tslint:disable-next-line:no-any const closure = new Closure(node as any, currentFrame(context), context) @@ -366,7 +377,9 @@ export const evaluators: { [nodeType: string]: Evaluator } = { return undefined } }, - ExpressionStatement: expressionStatementEvaluator, + ExpressionStatement: function*(node: es.ExpressionStatement, context: Context) { + return yield* evaluate(node.expression, context) + }, *ReturnStatement(node: es.ReturnStatement, context: Context) { if (node.argument) { if (node.argument.type === 'CallExpression') { @@ -380,7 +393,7 @@ export const evaluators: { [nodeType: string]: Evaluator } = { return new ReturnValue(undefined) } }, - *WhileStatement(node: es.WhileStatement, context: Context) { + WhileStatement: function*(node: es.WhileStatement, context: Context) { let value: any // tslint:disable-line let test while ( @@ -397,7 +410,7 @@ export const evaluators: { [nodeType: string]: Evaluator } = { } return value }, - *ObjectExpression(node: es.ObjectExpression, context: Context) { + ObjectExpression: function*(node: es.ObjectExpression, context: Context) { const obj = {} for (const prop of node.properties) { let key @@ -410,7 +423,7 @@ export const evaluators: { [nodeType: string]: Evaluator } = { } return obj }, - *BlockStatement(node: es.BlockStatement, context: Context) { + BlockStatement: function*(node: es.BlockStatement, context: Context) { let result: Value for (const statement of node.body) { result = yield* evaluate(statement, context) @@ -424,7 +437,7 @@ export const evaluators: { [nodeType: string]: Evaluator } = { } return result }, - *Program(node: es.BlockStatement, context: Context) { + Program: function*(node: es.BlockStatement, context: Context) { let result: Value for (const statement of node.body) { result = yield* evaluate(statement, context) @@ -436,15 +449,6 @@ export const evaluators: { [nodeType: string]: Evaluator } = { } } -/** - * This is used as part of the evaluators object in this file, but must be - * defined outside, otherwise webpack parses it wrongly, resulting in a fatal - * syntax error: https://github.com/webpack/webpack/issues/7566 - */ -function* expressionStatementEvaluator(node: es.ExpressionStatement, context: Context) { - return yield* evaluate(node.expression, context) -} - export function* evaluate(node: es.Node, context: Context) { yield* visit(context, node) const result = yield* evaluators[node.type](node, context) From 11584fd7b129ddb9b916bbd5999e7b8fe1e3daf5 Mon Sep 17 00:00:00 2001 From: remo5000 Date: Thu, 21 Jun 2018 11:46:36 +0800 Subject: [PATCH 7/9] Use dynamic class name for greenscreen --- public/workspace-green.css | 27 ---------------------- src/components/Playground.tsx | 5 +---- src/styles/_workspaceGreen.scss | 40 +++++++++++++++++++++++++++++++++ src/styles/index.scss | 1 + 4 files changed, 42 insertions(+), 31 deletions(-) delete mode 100644 public/workspace-green.css create mode 100644 src/styles/_workspaceGreen.scss diff --git a/public/workspace-green.css b/public/workspace-green.css deleted file mode 100644 index 992f619469..0000000000 --- a/public/workspace-green.css +++ /dev/null @@ -1,27 +0,0 @@ -.NavigationBar { - display: none !important; } - -.workspace { - background: #00ff00 !important; } - -#brace-editor { - background: #00ff00 !important; } - -.ace_print-margin { - background: #00e000 !important; } - -/* editor specific */ -.editor-react-ace { - border: 0.1rem solid #00e000 !important; } - -.ace_gutter { - background: #00ff00 !important; } - -.ace_gutter-layer { - background: #00ff00 !important; } - -/* repl specific */ -.pt-card { - background: #00ff00 !important; - box-shadow: none !important; - border: 0.1rem solid #00e000 !important; } diff --git a/src/components/Playground.tsx b/src/components/Playground.tsx index 9317f79976..620b72737e 100644 --- a/src/components/Playground.tsx +++ b/src/components/Playground.tsx @@ -28,10 +28,7 @@ class Playground extends React.Component { public render() { return ( - - {this.state.isGreen ? ( - - ) : null} + Date: Thu, 21 Jun 2018 11:51:09 +0800 Subject: [PATCH 8/9] Format playground component --- src/components/Playground.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/Playground.tsx b/src/components/Playground.tsx index 620b72737e..2d067e0e8e 100644 --- a/src/components/Playground.tsx +++ b/src/components/Playground.tsx @@ -28,7 +28,11 @@ class Playground extends React.Component { public render() { return ( - + Date: Thu, 21 Jun 2018 12:23:16 +0800 Subject: [PATCH 9/9] Add console.log to report version --- .env.example | 1 + package.json | 2 +- src/index.tsx | 7 +++++++ src/sagas/index.ts | 2 +- src/utils/{secrets.ts => constants.ts} | 1 + 5 files changed, 11 insertions(+), 2 deletions(-) rename src/utils/{secrets.ts => constants.ts} (66%) diff --git a/.env.example b/.env.example index 053e8920fd..4db0c67413 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ REACT_APP_IVLE_KEY=your_ivle_key_here +REACT_APP_VERSION=$npm_package_version diff --git a/package.json b/package.json index d210d1d731..c8861ffba5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "cadet-frontend", - "version": "0.0.0", + "version": "0.1.0", "scripts-info": { "format": "Format source code", "start": "Start the Webpack development server", diff --git a/src/index.tsx b/src/index.tsx index 73b75d4e88..68fc539d44 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -6,6 +6,7 @@ import { ConnectedRouter } from 'react-router-redux' import ApplicationContainer from './containers/ApplicationContainer' import { store } from './createStore' +import { VERSION } from './utils/constants' import { history } from './utils/history' import registerServiceWorker from './utils/registerServiceWorker' @@ -13,6 +14,12 @@ import './styles/index.css' const rootContainer = document.getElementById('root') as HTMLElement ;(window as any).__REDUX_STORE__ = store // need this for slang's display +// tslint:disable-next-line +console.log( + `%c Source Academy v${VERSION}; ` + + 'Please visit https://github.com/source-academy/cadet-frontend/issues to report bugs or issues.', + 'font-weight: bold;' +) render( diff --git a/src/sagas/index.ts b/src/sagas/index.ts index c10d7ab110..855ce504d2 100644 --- a/src/sagas/index.ts +++ b/src/sagas/index.ts @@ -8,8 +8,8 @@ import * as actionTypes from '../actions/actionTypes' import { mockAssessmentOverviews, mockAssessments } from '../mocks/api' import { IState } from '../reducers/states' import { Context, interrupt, runInContext } from '../slang' +import { IVLE_KEY } from '../utils/constants' import { showSuccessMessage, showWarningMessage } from '../utils/notification' -import { IVLE_KEY } from '../utils/secrets' function* mainSaga() { yield* apiFetchSaga() diff --git a/src/utils/secrets.ts b/src/utils/constants.ts similarity index 66% rename from src/utils/secrets.ts rename to src/utils/constants.ts index 092f17fd5f..0dc7545bf0 100644 --- a/src/utils/secrets.ts +++ b/src/utils/constants.ts @@ -3,3 +3,4 @@ import * as dotenv from 'dotenv' dotenv.config() export const IVLE_KEY = process.env.REACT_APP_IVLE_KEY +export const VERSION = process.env.REACT_APP_VERSION