mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-14 10:08:06 -10:00
Compare commits
6
Commits
wer
...
@quin/functions
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4eb9ff37b | ||
|
|
ddfe541af2 | ||
|
|
cde1ab36d2 | ||
|
|
087074d831 | ||
|
|
365e2d7e07 | ||
|
|
1691f3271f |
No files matched your search
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "@tsconfig/node16/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"strict": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
overwrite: true
|
overwrite: true
|
||||||
schema: 'https://staging-api.expo.dev/graphql'
|
schema: 'http://localhost:3000/graphql'
|
||||||
documents:
|
documents:
|
||||||
- 'src/graphql/**/!(*.d).{ts,tsx}'
|
- 'src/graphql/**/!(*.d).{ts,tsx}'
|
||||||
- 'src/credentials/ios/api/graphql/**/!(*.d).{ts,tsx}'
|
- 'src/credentials/ios/api/graphql/**/!(*.d).{ts,tsx}'
|
||||||
|
|||||||
Generated
+347
@@ -2162,6 +2162,39 @@
|
|||||||
"name": "AccountUsageMetrics",
|
"name": "AccountUsageMetrics",
|
||||||
"description": null,
|
"description": null,
|
||||||
"fields": [
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "byBillingPeriod",
|
||||||
|
"description": null,
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "date",
|
||||||
|
"description": null,
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "DateTime",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultValue": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "UsageMetricTotal",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "metricsForServiceMetric",
|
"name": "metricsForServiceMetric",
|
||||||
"description": null,
|
"description": null,
|
||||||
@@ -13755,6 +13788,33 @@
|
|||||||
"enumValues": null,
|
"enumValues": null,
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "DeployFunctionResult",
|
||||||
|
"description": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "success",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Boolean",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": [],
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "OBJECT",
|
"kind": "OBJECT",
|
||||||
"name": "Deployment",
|
"name": "Deployment",
|
||||||
@@ -14564,6 +14624,210 @@
|
|||||||
"enumValues": null,
|
"enumValues": null,
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "FunctionMutation",
|
||||||
|
"description": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "createFunction",
|
||||||
|
"description": null,
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "appId",
|
||||||
|
"description": null,
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultValue": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bucketKey",
|
||||||
|
"description": null,
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultValue": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "DeployFunctionResult",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getFunctionStatus",
|
||||||
|
"description": null,
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "appId",
|
||||||
|
"description": null,
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultValue": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "FunctionStatusResult",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getSignedFunctionUploadSpecification",
|
||||||
|
"description": null,
|
||||||
|
"args": [
|
||||||
|
{
|
||||||
|
"name": "appId",
|
||||||
|
"description": null,
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"defaultValue": null,
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "GetFunctionUploadUrlResult",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": [],
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "FunctionStatusResult",
|
||||||
|
"description": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "logUrls",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "LIST",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "status",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": [],
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "GetFunctionUploadUrlResult",
|
||||||
|
"description": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "specification",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "String",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": [],
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "OBJECT",
|
"kind": "OBJECT",
|
||||||
"name": "GetSignedAssetUploadSpecificationsResult",
|
"name": "GetSignedAssetUploadSpecificationsResult",
|
||||||
@@ -19657,6 +19921,22 @@
|
|||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "function",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "FunctionMutation",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "googleServiceAccountKey",
|
"name": "googleServiceAccountKey",
|
||||||
"description": "Mutations that modify a Google Service Account Key",
|
"description": "Mutations that modify a Google Service Account Key",
|
||||||
@@ -22979,6 +23259,73 @@
|
|||||||
],
|
],
|
||||||
"possibleTypes": null
|
"possibleTypes": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "UsageMetricTotal",
|
||||||
|
"description": null,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "billingPeriod",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "BillingPeriod",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "metrics",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "LIST",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "OBJECT",
|
||||||
|
"name": "AccountUsageMetric",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "total",
|
||||||
|
"description": null,
|
||||||
|
"args": [],
|
||||||
|
"type": {
|
||||||
|
"kind": "NON_NULL",
|
||||||
|
"name": null,
|
||||||
|
"ofType": {
|
||||||
|
"kind": "SCALAR",
|
||||||
|
"name": "Float",
|
||||||
|
"ofType": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputFields": null,
|
||||||
|
"interfaces": [],
|
||||||
|
"enumValues": null,
|
||||||
|
"possibleTypes": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"kind": "ENUM",
|
"kind": "ENUM",
|
||||||
"name": "UsageMetricType",
|
"name": "UsageMetricType",
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
"@expo/eas-json": "0.52.0",
|
"@expo/eas-json": "0.52.0",
|
||||||
"@expo/json-file": "8.2.34",
|
"@expo/json-file": "8.2.34",
|
||||||
"@expo/multipart-body-parser": "1.1.0",
|
"@expo/multipart-body-parser": "1.1.0",
|
||||||
|
"@expo/package-manager": "0.0.50",
|
||||||
"@expo/pkcs12": "0.0.7",
|
"@expo/pkcs12": "0.0.7",
|
||||||
"@expo/plist": "0.0.17",
|
"@expo/plist": "0.0.17",
|
||||||
"@expo/plugin-warn-if-update-available": "2.1.0",
|
"@expo/plugin-warn-if-update-available": "2.1.0",
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
"@oclif/plugin-help": "5.1.12",
|
"@oclif/plugin-help": "5.1.12",
|
||||||
"@urql/core": "2.4.3",
|
"@urql/core": "2.4.3",
|
||||||
"@urql/exchange-retry": "0.3.2",
|
"@urql/exchange-retry": "0.3.2",
|
||||||
|
"adm-zip": "0.5.9",
|
||||||
"chalk": "4.1.2",
|
"chalk": "4.1.2",
|
||||||
"cli-progress": "3.10.0",
|
"cli-progress": "3.10.0",
|
||||||
"cli-table3": "0.6.1",
|
"cli-table3": "0.6.1",
|
||||||
@@ -42,6 +44,7 @@
|
|||||||
"form-data": "4.0.0",
|
"form-data": "4.0.0",
|
||||||
"fs-extra": "10.0.1",
|
"fs-extra": "10.0.1",
|
||||||
"getenv": "1.0.0",
|
"getenv": "1.0.0",
|
||||||
|
"got": "^11.1.4",
|
||||||
"gradle-to-js": "2.0.0",
|
"gradle-to-js": "2.0.0",
|
||||||
"graphql": "16.3.0",
|
"graphql": "16.3.0",
|
||||||
"graphql-tag": "2.12.6",
|
"graphql-tag": "2.12.6",
|
||||||
@@ -49,6 +52,7 @@
|
|||||||
"ignore": "5.2.0",
|
"ignore": "5.2.0",
|
||||||
"indent-string": "4.0.0",
|
"indent-string": "4.0.0",
|
||||||
"jks-js": "1.0.2",
|
"jks-js": "1.0.2",
|
||||||
|
"js-yaml": "^3.13.1",
|
||||||
"joi": "17.6.0",
|
"joi": "17.6.0",
|
||||||
"keychain": "1.3.0",
|
"keychain": "1.3.0",
|
||||||
"log-symbols": "4.1.0",
|
"log-symbols": "4.1.0",
|
||||||
@@ -62,16 +66,20 @@
|
|||||||
"pkg-dir": "4.2.0",
|
"pkg-dir": "4.2.0",
|
||||||
"prompts": "2.4.2",
|
"prompts": "2.4.2",
|
||||||
"qrcode-terminal": "0.12.0",
|
"qrcode-terminal": "0.12.0",
|
||||||
|
"pnp-webpack-plugin": "1.7.0",
|
||||||
|
"react-dev-utils": "12.0.0",
|
||||||
"resolve-from": "5.0.0",
|
"resolve-from": "5.0.0",
|
||||||
"semver": "7.3.5",
|
"semver": "7.3.5",
|
||||||
"slash": "3.0.0",
|
"slash": "3.0.0",
|
||||||
"strip-ansi": "6.0.0",
|
"strip-ansi": "6.0.0",
|
||||||
"tar": "6.1.11",
|
"tar": "6.1.11",
|
||||||
"terminal-link": "2.1.1",
|
"terminal-link": "2.1.1",
|
||||||
|
"ts-loader": "^9.2.8",
|
||||||
"tslib": "2.3.1",
|
"tslib": "2.3.1",
|
||||||
"turndown": "7.1.1",
|
"turndown": "7.1.1",
|
||||||
"untildify": "4.0.0",
|
"untildify": "4.0.0",
|
||||||
"uuid": "8.3.2",
|
"uuid": "8.3.2",
|
||||||
|
"webpack": "5.72.0",
|
||||||
"wrap-ansi": "7.0.0"
|
"wrap-ansi": "7.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -80,10 +88,12 @@
|
|||||||
"@graphql-codegen/typescript": "2.4.8",
|
"@graphql-codegen/typescript": "2.4.8",
|
||||||
"@graphql-codegen/typescript-operations": "2.3.5",
|
"@graphql-codegen/typescript-operations": "2.3.5",
|
||||||
"@types/cli-progress": "3.9.2",
|
"@types/cli-progress": "3.9.2",
|
||||||
|
"@types/adm-zip": "0.4.34",
|
||||||
"@types/dateformat": "3.0.1",
|
"@types/dateformat": "3.0.1",
|
||||||
"@types/envinfo": "7.8.1",
|
"@types/envinfo": "7.8.1",
|
||||||
"@types/fs-extra": "9.0.13",
|
"@types/fs-extra": "9.0.13",
|
||||||
"@types/getenv": "^1.0.0",
|
"@types/getenv": "^1.0.0",
|
||||||
|
"@types/js-yaml": "^3.12.2",
|
||||||
"@types/mime": "2.0.3",
|
"@types/mime": "2.0.3",
|
||||||
"@types/node-fetch": "^2.5.7",
|
"@types/node-fetch": "^2.5.7",
|
||||||
"@types/node-forge": "1.0.1",
|
"@types/node-forge": "1.0.1",
|
||||||
@@ -92,6 +102,7 @@
|
|||||||
"@types/tar": "6.1.1",
|
"@types/tar": "6.1.1",
|
||||||
"@types/tough-cookie": "4.0.1",
|
"@types/tough-cookie": "4.0.1",
|
||||||
"@types/uuid": "8.3.4",
|
"@types/uuid": "8.3.4",
|
||||||
|
"@types/webpack": "5.28.0",
|
||||||
"@types/wrap-ansi": "3.0.0",
|
"@types/wrap-ansi": "3.0.0",
|
||||||
"axios": "0.26.1",
|
"axios": "0.26.1",
|
||||||
"eslint-plugin-graphql": "4.0.0",
|
"eslint-plugin-graphql": "4.0.0",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import admzip from 'adm-zip';
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
@@ -122,6 +123,63 @@ export async function makeProjectTarballAsync(): Promise<{ path: string; size: n
|
|||||||
return { size, path: tarPath };
|
return { size, path: tarPath };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function makeZipAsync(pathToZip: string): Promise<{ path: string; size: number }> {
|
||||||
|
const spinner = ora('Compressing project files');
|
||||||
|
|
||||||
|
await fs.mkdirp(getTmpDirectory());
|
||||||
|
const shallowClonePath = path.join(getTmpDirectory(), `${uuidv4()}-shallow-clone`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
process.chdir(pathToZip);
|
||||||
|
await getVcsClient().makeShallowCopyAsync(shallowClonePath);
|
||||||
|
process.chdir('..');
|
||||||
|
|
||||||
|
const zipPath = path.join(getTmpDirectory(), `${uuidv4()}.zip`);
|
||||||
|
|
||||||
|
// If the compression takes longer then a second, show the spinner.
|
||||||
|
// This can happen when the user has a lot of resources or doesn't ignore their CocoaPods.
|
||||||
|
// A basic project on a Mac can compress in roughly ~40ms.
|
||||||
|
// A fairly complex project without CocoaPods ignored can take up to 30s.
|
||||||
|
const timer = setTimeout(
|
||||||
|
() => {
|
||||||
|
spinner.start();
|
||||||
|
},
|
||||||
|
Log.isDebug ? 1 : 1000
|
||||||
|
);
|
||||||
|
// TODO: Possibly warn after more time about unoptimized assets.
|
||||||
|
const compressTimerLabel = 'makeProjectZipAsync';
|
||||||
|
startTimer(compressTimerLabel);
|
||||||
|
|
||||||
|
const zip = new admzip();
|
||||||
|
zip.addLocalFolder(shallowClonePath);
|
||||||
|
await zipAsync(zip, zipPath);
|
||||||
|
clearTimeout(timer);
|
||||||
|
|
||||||
|
const { size } = await fs.stat(zipPath);
|
||||||
|
const duration = endTimer(compressTimerLabel);
|
||||||
|
if (spinner.isSpinning) {
|
||||||
|
const prettyTime = formatMilliseconds(duration);
|
||||||
|
spinner.succeed(
|
||||||
|
`Compressed project files ${chalk.dim(`${prettyTime} (${formatBytes(size)})`)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { size, path: zipPath };
|
||||||
|
} catch (err) {
|
||||||
|
if (spinner.isSpinning) {
|
||||||
|
spinner.fail();
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
} finally {
|
||||||
|
await fs.remove(shallowClonePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function zipAsync(zip: admzip, targetFileName?: string): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
zip.writeZip(targetFileName, (error: Error | null) => (error ? reject(error) : resolve()));
|
||||||
|
});
|
||||||
|
}
|
||||||
enum ShouldCommitChanges {
|
enum ShouldCommitChanges {
|
||||||
Yes,
|
Yes,
|
||||||
ShowDiffFirst,
|
ShowDiffFirst,
|
||||||
|
|||||||
@@ -0,0 +1,151 @@
|
|||||||
|
import { getConfig } from '@expo/config';
|
||||||
|
import spawnAsync, { SpawnOptions, SpawnResult } from '@expo/spawn-async';
|
||||||
|
import admzip from 'adm-zip';
|
||||||
|
import fs from 'fs';
|
||||||
|
import fsPromise from 'fs/promises';
|
||||||
|
import got from 'got';
|
||||||
|
import fetch from 'node-fetch';
|
||||||
|
import path from 'path';
|
||||||
|
import { pipeline } from 'stream/promises';
|
||||||
|
|
||||||
|
import { makeZipAsync } from '../../build/utils/repository';
|
||||||
|
import EasCommand from '../../commandUtils/EasCommand';
|
||||||
|
import { bundleFunctionsAsync, prepareFunctionEntriesAsync } from '../../functions/webpack';
|
||||||
|
import { FunctionMutation } from '../../graphql/mutations/FunctionMutation';
|
||||||
|
import { PresignedPost } from '../../graphql/mutations/UploadSessionMutation';
|
||||||
|
import Log from '../../log';
|
||||||
|
import { ora } from '../../ora';
|
||||||
|
import {
|
||||||
|
findProjectRootAsync,
|
||||||
|
getProjectAccountName,
|
||||||
|
getProjectIdAsync,
|
||||||
|
} from '../../project/projectUtils';
|
||||||
|
import { uploadWithPresignedPostAsync } from '../../uploads';
|
||||||
|
import { ensureLoggedInAsync } from '../../user/actions';
|
||||||
|
import { expoCommandAsync } from '../../utils/expoCli';
|
||||||
|
import { sleepAsync } from '../../utils/promise';
|
||||||
|
|
||||||
|
export default class FunctionsCreate extends EasCommand {
|
||||||
|
static description = 'create a function';
|
||||||
|
|
||||||
|
async runAsync(): Promise<void> {
|
||||||
|
const projectDir = await findProjectRootAsync();
|
||||||
|
const { exp } = getConfig(projectDir, { skipSDKVersionRequirement: true });
|
||||||
|
const projectId = await getProjectIdAsync(exp);
|
||||||
|
|
||||||
|
const downloadSpinner = ora('Downloading functions template...').start();
|
||||||
|
const functionsTemplateUrl =
|
||||||
|
'https://github.com/quinlanj/functions-entry-point/archive/refs/heads/main.zip';
|
||||||
|
const functionsBuild = 'functions-build';
|
||||||
|
const functionsBuildZip = `${functionsBuild}.zip`;
|
||||||
|
const originalUnzippedDirectory = 'functions-entry-point-main';
|
||||||
|
// ensure empty build directory
|
||||||
|
await fsPromise.rm(path.join(projectDir, functionsBuild), { force: true, recursive: true });
|
||||||
|
// download and unzip the functions template
|
||||||
|
const downloadStream = got.stream(functionsTemplateUrl);
|
||||||
|
await pipeline(downloadStream, fs.createWriteStream(path.join(projectDir, functionsBuildZip)));
|
||||||
|
const zip = new admzip(functionsBuildZip);
|
||||||
|
zip.extractAllTo(/*target path*/ path.join(projectDir), /*overwrite*/ true);
|
||||||
|
// rename to desired dir, remove zip
|
||||||
|
await Promise.all([
|
||||||
|
fsPromise.rename(
|
||||||
|
path.join(projectDir, originalUnzippedDirectory),
|
||||||
|
path.join(projectDir, functionsBuild)
|
||||||
|
),
|
||||||
|
fsPromise.rm(path.join(projectDir, functionsBuildZip)),
|
||||||
|
]);
|
||||||
|
downloadSpinner.succeed('Downloaded functions template');
|
||||||
|
|
||||||
|
const permissionSpinner = ora('Modifying permissions...').start();
|
||||||
|
const promises = [];
|
||||||
|
for (const file of await fsPromise.readdir(path.join(projectDir, functionsBuild, 'scripts'))) {
|
||||||
|
promises.push(fsPromise.chmod(path.join(projectDir, functionsBuild, 'scripts', file), 0o777));
|
||||||
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
|
permissionSpinner.succeed('Modified permissions');
|
||||||
|
|
||||||
|
const gitSpinner = ora('Initializing inner git...').start();
|
||||||
|
process.chdir(path.join(projectDir, functionsBuild));
|
||||||
|
const { status, stderr } = await spawnAsync('git', ['init']);
|
||||||
|
if (status !== 0) {
|
||||||
|
throw new Error('Failed to initialize git: ' + stderr);
|
||||||
|
}
|
||||||
|
process.chdir('..');
|
||||||
|
gitSpinner.succeed('Initialized inner git');
|
||||||
|
|
||||||
|
Log.log('Bundling functions...');
|
||||||
|
const functionDirectory = path.join(projectDir, 'functions');
|
||||||
|
const functionEntries = await prepareFunctionEntriesAsync(functionDirectory);
|
||||||
|
const outputDirectory = path.join(projectDir, functionsBuild, 'functions');
|
||||||
|
await bundleFunctionsAsync(projectDir, functionEntries, outputDirectory);
|
||||||
|
Log.log('Successfully bundled functions...');
|
||||||
|
|
||||||
|
Log.log('Doing web build...');
|
||||||
|
const accountName = getProjectAccountName(exp, await ensureLoggedInAsync());
|
||||||
|
const functionName = `${accountName}-${exp.slug}`; // TODO: set up a Cloudflare enterprise domain so we don't have to do this
|
||||||
|
const region = 'us-east1';
|
||||||
|
const cloudFunctionProject = 'eas-functions';
|
||||||
|
const publicUrl = `https://${region}-${cloudFunctionProject}.cloudfunctions.net/${functionName}`;
|
||||||
|
const webOutputDirectory = path.join(projectDir, functionsBuild, 'web-build');
|
||||||
|
await expoCommandAsync(projectDir, ['build:web'], {
|
||||||
|
env: { WEBPACK_BUILD_OUTPUT_PATH: webOutputDirectory, WEB_PUBLIC_URL: publicUrl },
|
||||||
|
});
|
||||||
|
Log.log('Finished web build...');
|
||||||
|
|
||||||
|
const { path: compressedProjectPath } = await makeZipAsync(
|
||||||
|
path.join(projectDir, functionsBuild)
|
||||||
|
);
|
||||||
|
const uploadSpinner = ora('Uploading project...').start();
|
||||||
|
let bucketKey;
|
||||||
|
try {
|
||||||
|
const specification = await FunctionMutation.uploadAsync(projectId);
|
||||||
|
const presignedPost: PresignedPost = JSON.parse(specification);
|
||||||
|
await uploadWithPresignedPostAsync(compressedProjectPath, presignedPost);
|
||||||
|
bucketKey = presignedPost.fields.key;
|
||||||
|
uploadSpinner.succeed(`Successfully uploaded project to bucket: ${bucketKey}`);
|
||||||
|
} catch (err) {
|
||||||
|
uploadSpinner.fail('Failed to upload project');
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
const spinner = ora('Creating a function...').start();
|
||||||
|
try {
|
||||||
|
const success = await FunctionMutation.createAsync(bucketKey, projectId);
|
||||||
|
spinner.succeed(`Successfully created function: ${success}`);
|
||||||
|
} catch (err) {
|
||||||
|
spinner.fail('Failed to create a function');
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.log('Streaming build logs....');
|
||||||
|
const logsPrinted = new Set<string>();
|
||||||
|
let sawLastLog = false;
|
||||||
|
while (true) {
|
||||||
|
const { status, logUrls } = await FunctionMutation.getStatusAsync(projectId);
|
||||||
|
Log.log(`Function status: ${status}`);
|
||||||
|
|
||||||
|
const logsUrlsToPrint = logUrls.filter(url => !logsPrinted.has(url)).sort();
|
||||||
|
const logResponses = await Promise.all(logsUrlsToPrint.map(url => fetch(url)));
|
||||||
|
const logBodies = await Promise.all(logResponses.map(body => body.text()));
|
||||||
|
const logPayloads = logBodies.map(body => JSON.parse(body)).map(body => body.textPayload);
|
||||||
|
logPayloads.forEach(response => Log.log(response));
|
||||||
|
logsUrlsToPrint.forEach(logUrl => logsPrinted.add(logUrl));
|
||||||
|
if (!sawLastLog) {
|
||||||
|
sawLastLog = logPayloads.some(log => log === 'DONE');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sawLastLog && status === 'ACTIVE') {
|
||||||
|
Log.log(`🎉🎉🎉 Hooray! Your function is complete!`);
|
||||||
|
Log.log(`🎉🎉🎉 Your web-app is available at: ${publicUrl}`);
|
||||||
|
for (const submodule of Object.keys(functionEntries)) {
|
||||||
|
Log.log(`🎉🎉🎉 ${submodule} is available at: ${publicUrl}/functions/${submodule}`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else if (status !== 'ACTIVE' && status !== 'DEPLOY_IN_PROGRESS') {
|
||||||
|
Log.log(`🤔 Uh oh...your function is ${status}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await sleepAsync(6000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import EasCommand from '../../commandUtils/EasCommand';
|
||||||
|
import { installNodeDependenciesAsync } from '../../functions/CreateApp';
|
||||||
|
import { YarnPackageManager } from '../../functions/PackageManager';
|
||||||
|
import Log from '../../log';
|
||||||
|
import { ora } from '../../ora';
|
||||||
|
import { findProjectRootAsync } from '../../project/projectUtils';
|
||||||
|
|
||||||
|
export default class FunctionsCreate extends EasCommand {
|
||||||
|
static description = 'start a function';
|
||||||
|
|
||||||
|
async runAsync(): Promise<void> {
|
||||||
|
const projectDir = await findProjectRootAsync();
|
||||||
|
const functionsBuild = 'functions-build';
|
||||||
|
|
||||||
|
const installSpinner = ora('Installing dependencies...').start();
|
||||||
|
await installNodeDependenciesAsync(path.join(projectDir, functionsBuild), 'yarn');
|
||||||
|
installSpinner.succeed('Modified permissions');
|
||||||
|
|
||||||
|
Log.log('serving up the function...');
|
||||||
|
const yarn = new YarnPackageManager({ cwd: path.join(projectDir, functionsBuild) });
|
||||||
|
await yarn.runAsync('dev');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import * as PackageManager from '@expo/package-manager';
|
||||||
|
import fs from 'fs/promises';
|
||||||
|
import yaml from 'js-yaml';
|
||||||
|
import * as path from 'path';
|
||||||
|
import semver from 'semver';
|
||||||
|
|
||||||
|
import Log from '../log';
|
||||||
|
|
||||||
|
export async function installNodeDependenciesAsync(
|
||||||
|
projectRoot: string,
|
||||||
|
packageManager: 'yarn' | 'npm'
|
||||||
|
): Promise<void> {
|
||||||
|
const options = { cwd: projectRoot };
|
||||||
|
if (packageManager === 'yarn') {
|
||||||
|
const yarn = new PackageManager.YarnPackageManager(options);
|
||||||
|
const version = await yarn.versionAsync();
|
||||||
|
const nodeLinker = await yarn.getConfigAsync('nodeLinker');
|
||||||
|
if (semver.satisfies(version, '>=2.0.0-rc.24') && nodeLinker !== 'node-modules') {
|
||||||
|
const yarnRc = path.join(projectRoot, '.yarnrc.yml');
|
||||||
|
let yamlString = '';
|
||||||
|
try {
|
||||||
|
yamlString = await fs.readFile(yarnRc, 'utf8');
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.code !== 'ENOENT') {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const config: any = yamlString ? yaml.safeLoad(yamlString) : {};
|
||||||
|
config.nodeLinker = 'node-modules';
|
||||||
|
|
||||||
|
Log.warn(
|
||||||
|
`Yarn v${version} detected, enabling experimental Yarn v2 support using the node-modules plugin.`
|
||||||
|
);
|
||||||
|
Log.log(`Writing ${yarnRc}...`);
|
||||||
|
await fs.writeFile(yarnRc, yaml.safeDump(config));
|
||||||
|
}
|
||||||
|
await yarn.installAsync();
|
||||||
|
} else {
|
||||||
|
await new PackageManager.NpmPackageManager(options).installAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import spawnAsync, { SpawnOptions, SpawnResult } from '@expo/spawn-async';
|
||||||
|
import { Transform } from 'stream';
|
||||||
|
|
||||||
|
import Log from '../log';
|
||||||
|
|
||||||
|
function ansiRegex({ onlyFirst = false } = {}): RegExp {
|
||||||
|
const pattern = [
|
||||||
|
'[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
|
||||||
|
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))',
|
||||||
|
].join('|');
|
||||||
|
|
||||||
|
return new RegExp(pattern, onlyFirst ? undefined : 'g');
|
||||||
|
}
|
||||||
|
|
||||||
|
const ansi = `(?:${ansiRegex().source})*`;
|
||||||
|
const yarnPeerDependencyWarningPattern = new RegExp(
|
||||||
|
`${ansi}warning${ansi} "[^"]+" has (?:unmet|incorrect) peer dependency "[^"]+"\\.\n`,
|
||||||
|
'g'
|
||||||
|
);
|
||||||
|
|
||||||
|
class YarnStderrTransform extends Transform {
|
||||||
|
_transform(
|
||||||
|
chunk: Buffer,
|
||||||
|
_encoding: string,
|
||||||
|
callback: (error?: Error | null, data?: any) => void
|
||||||
|
): void {
|
||||||
|
this.push(chunk.toString().replace(yarnPeerDependencyWarningPattern, ''));
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class YarnPackageManager {
|
||||||
|
options: SpawnOptions;
|
||||||
|
|
||||||
|
constructor({ cwd, silent }: { cwd: string; silent?: boolean }) {
|
||||||
|
this.options = {
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
},
|
||||||
|
cwd,
|
||||||
|
...(silent
|
||||||
|
? { ignoreStdio: true }
|
||||||
|
: {
|
||||||
|
stdio: ['inherit', 'inherit', 'pipe'],
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async runAsync(command: string): Promise<void> {
|
||||||
|
await this.runPrivateAsync([command]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Private
|
||||||
|
private async runPrivateAsync(args: string[]): Promise<SpawnResult> {
|
||||||
|
if (!this.options.ignoreStdio) {
|
||||||
|
Log.log(`> yarn ${args.join(' ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Have spawnAsync consume stdio but we don't actually do anything with it if it's ignored
|
||||||
|
const promise = spawnAsync('yarnpkg', args, { ...this.options, ignoreStdio: false });
|
||||||
|
if (promise.child.stderr && !this.options.ignoreStdio) {
|
||||||
|
promise.child.stderr.pipe(new YarnStderrTransform()).pipe(process.stderr);
|
||||||
|
}
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const friendlySyntaxErrorLabel = 'Syntax error:';
|
||||||
|
|
||||||
|
function isLikelyASyntaxError(message: string | string[]): boolean {
|
||||||
|
return message.indexOf(friendlySyntaxErrorLabel) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleans up webpack error messages.
|
||||||
|
function formatMessage(message: any): any {
|
||||||
|
let lines = [];
|
||||||
|
|
||||||
|
if (typeof message === 'string') {
|
||||||
|
lines = message.split('\n');
|
||||||
|
} else if ('message' in message) {
|
||||||
|
lines = message['message'].split('\n');
|
||||||
|
} else if (Array.isArray(message)) {
|
||||||
|
message.forEach(message => {
|
||||||
|
if ('message' in message) {
|
||||||
|
lines = message['message'].split('\n');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip webpack-added headers off errors/warnings
|
||||||
|
// https://github.com/webpack/webpack/blob/master/lib/ModuleError.js
|
||||||
|
lines = lines.filter((line: string) => !/Module [A-z ]+\(from/.test(line));
|
||||||
|
|
||||||
|
// Transform parsing error into syntax error
|
||||||
|
// TODO: move this to our ESLint formatter?
|
||||||
|
lines = lines.map((line: string) => {
|
||||||
|
const parsingError = /Line (\d+):(?:(\d+):)?\s*Parsing error: (.+)$/.exec(line);
|
||||||
|
if (!parsingError) {
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
const [, errorLine, errorColumn, errorMessage] = parsingError;
|
||||||
|
return `${friendlySyntaxErrorLabel} ${errorMessage} (${errorLine}:${errorColumn})`;
|
||||||
|
});
|
||||||
|
|
||||||
|
message = lines.join('\n');
|
||||||
|
// Smoosh syntax errors (commonly found in CSS)
|
||||||
|
message = message.replace(
|
||||||
|
/SyntaxError\s+\((\d+):(\d+)\)\s*(.+?)\n/g,
|
||||||
|
`${friendlySyntaxErrorLabel} $3 ($1:$2)\n`
|
||||||
|
);
|
||||||
|
// Clean up export errors
|
||||||
|
message = message.replace(
|
||||||
|
/^.*export '(.+?)' was not found in '(.+?)'.*$/gm,
|
||||||
|
`Attempted import error: '$1' is not exported from '$2'.`
|
||||||
|
);
|
||||||
|
message = message.replace(
|
||||||
|
/^.*export 'default' \(imported as '(.+?)'\) was not found in '(.+?)'.*$/gm,
|
||||||
|
`Attempted import error: '$2' does not contain a default export (imported as '$1').`
|
||||||
|
);
|
||||||
|
message = message.replace(
|
||||||
|
/^.*export '(.+?)' \(imported as '(.+?)'\) was not found in '(.+?)'.*$/gm,
|
||||||
|
`Attempted import error: '$1' is not exported from '$3' (imported as '$2').`
|
||||||
|
);
|
||||||
|
lines = message.split('\n');
|
||||||
|
|
||||||
|
// Remove leading newline
|
||||||
|
if (lines.length > 2 && lines[1].trim() === '') {
|
||||||
|
lines.splice(1, 1);
|
||||||
|
}
|
||||||
|
// Clean up file name
|
||||||
|
lines[0] = lines[0].replace(/^(.*) \d+:\d+-\d+$/, '$1');
|
||||||
|
|
||||||
|
// Cleans up verbose "module not found" messages for files and packages.
|
||||||
|
if (lines[1] && lines[1].indexOf('Module not found: ') === 0) {
|
||||||
|
lines = [
|
||||||
|
lines[0],
|
||||||
|
lines[1]
|
||||||
|
.replace('Error: ', '')
|
||||||
|
.replace('Module not found: Cannot find file:', 'Cannot find file:'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add helpful message for users trying to use Sass for the first time
|
||||||
|
if (lines[1] && lines[1].match(/Cannot find module.+sass/)) {
|
||||||
|
lines[1] = 'To import Sass files, you first need to install sass.\n';
|
||||||
|
lines[1] += 'Run `npm install sass` or `yarn add sass` inside your workspace.';
|
||||||
|
}
|
||||||
|
|
||||||
|
message = lines.join('\n');
|
||||||
|
// Internal stacks are generally useless so we strip them... with the
|
||||||
|
// exception of stacks containing `webpack:` because they're normally
|
||||||
|
// from user code generated by webpack. For more information see
|
||||||
|
// https://github.com/facebook/create-react-app/pull/1050
|
||||||
|
message = message.replace(/^\s*at\s((?!webpack:).)*:\d+:\d+[\s)]*(\n|$)/gm, ''); // at ... ...:x:y
|
||||||
|
message = message.replace(/^\s*at\s<anonymous>(\n|$)/gm, ''); // at <anonymous>
|
||||||
|
lines = message.split('\n');
|
||||||
|
|
||||||
|
// Remove duplicated newlines
|
||||||
|
lines = lines.filter(
|
||||||
|
(line: string, index: number, arr: string[]) =>
|
||||||
|
index === 0 || line.trim() !== '' || line.trim() !== arr[index - 1].trim()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reassemble the message
|
||||||
|
message = lines.join('\n');
|
||||||
|
return message.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatWebpackMessages(json: any): {
|
||||||
|
errors: any[];
|
||||||
|
warnings: any[];
|
||||||
|
} {
|
||||||
|
const formattedErrors = json.errors.map(formatMessage);
|
||||||
|
const formattedWarnings = json.warnings.map(formatMessage);
|
||||||
|
const result = { errors: formattedErrors, warnings: formattedWarnings };
|
||||||
|
if (result.errors.some(isLikelyASyntaxError)) {
|
||||||
|
// If there are any syntax errors, show just them.
|
||||||
|
result.errors = result.errors.filter(isLikelyASyntaxError);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
import chalk from 'chalk';
|
||||||
|
import fs from 'fs/promises';
|
||||||
|
import path from 'path';
|
||||||
|
import PnpWebpackPlugin from 'pnp-webpack-plugin';
|
||||||
|
import { Compiler, Configuration, EntryObject, webpack } from 'webpack';
|
||||||
|
|
||||||
|
import Log from '../log';
|
||||||
|
import { formatWebpackMessages } from './formatWebpackMessages';
|
||||||
|
|
||||||
|
async function getWebpackFunctionConfigurationAsync(
|
||||||
|
projectDir: string,
|
||||||
|
entry: EntryObject,
|
||||||
|
outputPath: string
|
||||||
|
): Promise<Configuration> {
|
||||||
|
const webpackConfig = {
|
||||||
|
entry,
|
||||||
|
target: 'node',
|
||||||
|
mode: 'development' as 'development',
|
||||||
|
// Disable file info logs.
|
||||||
|
//stats: 'none',
|
||||||
|
|
||||||
|
// https://webpack.js.org/configuration/other-options/#bail
|
||||||
|
// Fail out on the first error instead of tolerating it.
|
||||||
|
bail: true,
|
||||||
|
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
use: {
|
||||||
|
loader: require.resolve('ts-loader'),
|
||||||
|
options: {
|
||||||
|
configFile: require.resolve('../../functions.tsconfig.webpack.json'),
|
||||||
|
context: projectDir,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exclude: /node_modules/,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
plugins: [PnpWebpackPlugin],
|
||||||
|
extensions: ['.tsx', '.ts', '.js'],
|
||||||
|
},
|
||||||
|
resolveLoader: {
|
||||||
|
plugins: [PnpWebpackPlugin.moduleLoader(module)],
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
filename: '[name].js',
|
||||||
|
path: outputPath,
|
||||||
|
library: {
|
||||||
|
type: 'commonjs',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return webpackConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function compileWebAppAsync(compiler: Compiler): Promise<any> {
|
||||||
|
// We generate the stats.json file in the webpack-config
|
||||||
|
const { warnings } = await new Promise((resolve, reject) =>
|
||||||
|
compiler.run((error, stats) => {
|
||||||
|
let messages;
|
||||||
|
if (error) {
|
||||||
|
if (!error.message) {
|
||||||
|
return reject(error);
|
||||||
|
}
|
||||||
|
messages = formatWebpackMessages({
|
||||||
|
errors: [error.message],
|
||||||
|
warnings: [],
|
||||||
|
_showErrors: true,
|
||||||
|
_showWarnings: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
messages = formatWebpackMessages(
|
||||||
|
stats?.toJson({ all: false, warnings: true, errors: true })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messages.errors.length) {
|
||||||
|
// Only keep the first error. Others are often indicative
|
||||||
|
// of the same problem, but confuse the reader with noise.
|
||||||
|
if (messages.errors.length > 1) {
|
||||||
|
messages.errors.length = 1;
|
||||||
|
}
|
||||||
|
return reject(messages.errors.join('\n\n'));
|
||||||
|
}
|
||||||
|
resolve({
|
||||||
|
warnings: messages.warnings,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return { warnings };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function bundleFunctionsAsync(
|
||||||
|
projectDir: string,
|
||||||
|
entry: EntryObject,
|
||||||
|
outputPath: string
|
||||||
|
): Promise<void> {
|
||||||
|
const config = await getWebpackFunctionConfigurationAsync(projectDir, entry, outputPath);
|
||||||
|
const compiler = webpack(config);
|
||||||
|
try {
|
||||||
|
const { warnings } = await compileWebAppAsync(compiler);
|
||||||
|
if (warnings.length) {
|
||||||
|
Log.warn(chalk.yellow('Compiled with warnings.\n'));
|
||||||
|
Log.warn(warnings.join('\n\n'));
|
||||||
|
} else {
|
||||||
|
Log.log(chalk.green('Compiled successfully.\n'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Log.error(chalk.red('Failed to compile.\n'));
|
||||||
|
Log.error(error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function prepareFunctionEntriesAsync(functionsPath: string): Promise<EntryObject> {
|
||||||
|
const files = await fs.readdir(functionsPath);
|
||||||
|
if (files.length === 0) {
|
||||||
|
throw new Error(`No functions found in ${functionsPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const entries = {} as EntryObject;
|
||||||
|
for (const file of files) {
|
||||||
|
const filenameNoExtension = path.parse(file).name;
|
||||||
|
entries[filenameNoExtension] = path.join(functionsPath, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
+75
@@ -401,10 +401,16 @@ export type AccountUsageMetric = {
|
|||||||
|
|
||||||
export type AccountUsageMetrics = {
|
export type AccountUsageMetrics = {
|
||||||
__typename?: 'AccountUsageMetrics';
|
__typename?: 'AccountUsageMetrics';
|
||||||
|
byBillingPeriod: UsageMetricTotal;
|
||||||
metricsForServiceMetric: Array<AccountUsageMetric>;
|
metricsForServiceMetric: Array<AccountUsageMetric>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type AccountUsageMetricsByBillingPeriodArgs = {
|
||||||
|
date: Scalars['DateTime'];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type AccountUsageMetricsMetricsForServiceMetricArgs = {
|
export type AccountUsageMetricsMetricsForServiceMetricArgs = {
|
||||||
granularity: UsageMetricsGranularity;
|
granularity: UsageMetricsGranularity;
|
||||||
metric: EasServiceMetric;
|
metric: EasServiceMetric;
|
||||||
@@ -2019,6 +2025,11 @@ export type DeleteWebhookResult = {
|
|||||||
id: Scalars['ID'];
|
id: Scalars['ID'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DeployFunctionResult = {
|
||||||
|
__typename?: 'DeployFunctionResult';
|
||||||
|
success: Scalars['Boolean'];
|
||||||
|
};
|
||||||
|
|
||||||
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
|
/** Represents a Deployment - a set of Builds with the same Runtime Version and Channel */
|
||||||
export type Deployment = {
|
export type Deployment = {
|
||||||
__typename?: 'Deployment';
|
__typename?: 'Deployment';
|
||||||
@@ -2149,6 +2160,40 @@ export enum Feature {
|
|||||||
Teams = 'TEAMS'
|
Teams = 'TEAMS'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type FunctionMutation = {
|
||||||
|
__typename?: 'FunctionMutation';
|
||||||
|
createFunction: DeployFunctionResult;
|
||||||
|
getFunctionStatus: FunctionStatusResult;
|
||||||
|
getSignedFunctionUploadSpecification: GetFunctionUploadUrlResult;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type FunctionMutationCreateFunctionArgs = {
|
||||||
|
appId: Scalars['String'];
|
||||||
|
bucketKey: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type FunctionMutationGetFunctionStatusArgs = {
|
||||||
|
appId: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type FunctionMutationGetSignedFunctionUploadSpecificationArgs = {
|
||||||
|
appId: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FunctionStatusResult = {
|
||||||
|
__typename?: 'FunctionStatusResult';
|
||||||
|
logUrls: Array<Scalars['String']>;
|
||||||
|
status: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetFunctionUploadUrlResult = {
|
||||||
|
__typename?: 'GetFunctionUploadUrlResult';
|
||||||
|
specification: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
export type GetSignedAssetUploadSpecificationsResult = {
|
export type GetSignedAssetUploadSpecificationsResult = {
|
||||||
__typename?: 'GetSignedAssetUploadSpecificationsResult';
|
__typename?: 'GetSignedAssetUploadSpecificationsResult';
|
||||||
specifications: Array<Scalars['String']>;
|
specifications: Array<Scalars['String']>;
|
||||||
@@ -2866,6 +2911,7 @@ export type RootMutation = {
|
|||||||
emailSubscription: EmailSubscriptionMutation;
|
emailSubscription: EmailSubscriptionMutation;
|
||||||
/** Mutations that create and delete EnvironmentSecrets */
|
/** Mutations that create and delete EnvironmentSecrets */
|
||||||
environmentSecret: EnvironmentSecretMutation;
|
environmentSecret: EnvironmentSecretMutation;
|
||||||
|
function: FunctionMutation;
|
||||||
/** Mutations that modify a Google Service Account Key */
|
/** Mutations that modify a Google Service Account Key */
|
||||||
googleServiceAccountKey: GoogleServiceAccountKeyMutation;
|
googleServiceAccountKey: GoogleServiceAccountKeyMutation;
|
||||||
/** Mutations that modify the build credentials for an iOS app */
|
/** Mutations that modify the build credentials for an iOS app */
|
||||||
@@ -3395,6 +3441,13 @@ export enum UploadSessionType {
|
|||||||
EasSubmitAppArchive = 'EAS_SUBMIT_APP_ARCHIVE'
|
EasSubmitAppArchive = 'EAS_SUBMIT_APP_ARCHIVE'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UsageMetricTotal = {
|
||||||
|
__typename?: 'UsageMetricTotal';
|
||||||
|
billingPeriod: BillingPeriod;
|
||||||
|
metrics: Array<AccountUsageMetric>;
|
||||||
|
total: Scalars['Float'];
|
||||||
|
};
|
||||||
|
|
||||||
export enum UsageMetricType {
|
export enum UsageMetricType {
|
||||||
Request = 'REQUEST'
|
Request = 'REQUEST'
|
||||||
}
|
}
|
||||||
@@ -4291,6 +4344,28 @@ export type DeleteEnvironmentSecretMutationVariables = Exact<{
|
|||||||
|
|
||||||
export type DeleteEnvironmentSecretMutation = { __typename?: 'RootMutation', environmentSecret: { __typename?: 'EnvironmentSecretMutation', deleteEnvironmentSecret: { __typename?: 'DeleteEnvironmentSecretResult', id: string } } };
|
export type DeleteEnvironmentSecretMutation = { __typename?: 'RootMutation', environmentSecret: { __typename?: 'EnvironmentSecretMutation', deleteEnvironmentSecret: { __typename?: 'DeleteEnvironmentSecretResult', id: string } } };
|
||||||
|
|
||||||
|
export type GetSignedUploadFunctionMutationVariables = Exact<{
|
||||||
|
appId: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type GetSignedUploadFunctionMutation = { __typename?: 'RootMutation', function: { __typename?: 'FunctionMutation', getSignedFunctionUploadSpecification: { __typename?: 'GetFunctionUploadUrlResult', specification: string } } };
|
||||||
|
|
||||||
|
export type CreateFunctionMutationVariables = Exact<{
|
||||||
|
bucketKey: Scalars['String'];
|
||||||
|
appId: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type CreateFunctionMutation = { __typename?: 'RootMutation', function: { __typename?: 'FunctionMutation', createFunction: { __typename?: 'DeployFunctionResult', success: boolean } } };
|
||||||
|
|
||||||
|
export type GetFunctionStatusMutationVariables = Exact<{
|
||||||
|
appId: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type GetFunctionStatusMutation = { __typename?: 'RootMutation', function: { __typename?: 'FunctionMutation', getFunctionStatus: { __typename?: 'FunctionStatusResult', status: string, logUrls: Array<string> } } };
|
||||||
|
|
||||||
export type CreateKeystoreGenerationUrlMutationVariables = Exact<{ [key: string]: never; }>;
|
export type CreateKeystoreGenerationUrlMutationVariables = Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import gql from 'graphql-tag';
|
||||||
|
|
||||||
|
import { graphqlClient, withErrorHandlingAsync } from '../client';
|
||||||
|
import {
|
||||||
|
CreateFunctionMutation,
|
||||||
|
CreateFunctionMutationVariables,
|
||||||
|
GetFunctionStatusMutation,
|
||||||
|
GetFunctionStatusMutationVariables,
|
||||||
|
GetSignedUploadFunctionMutation,
|
||||||
|
GetSignedUploadFunctionMutationVariables,
|
||||||
|
} from '../generated';
|
||||||
|
|
||||||
|
export const FunctionMutation = {
|
||||||
|
async uploadAsync(appId: string): Promise<string> {
|
||||||
|
const data = await withErrorHandlingAsync(
|
||||||
|
graphqlClient
|
||||||
|
.mutation<GetSignedUploadFunctionMutation, GetSignedUploadFunctionMutationVariables>(
|
||||||
|
gql`
|
||||||
|
mutation GetSignedUploadFunctionMutation($appId: String!) {
|
||||||
|
function {
|
||||||
|
getSignedFunctionUploadSpecification(appId: $appId) {
|
||||||
|
specification
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
appId,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
);
|
||||||
|
return data.function.getSignedFunctionUploadSpecification.specification;
|
||||||
|
},
|
||||||
|
async createAsync(bucketKey: string, appId: string): Promise<boolean> {
|
||||||
|
const data = await withErrorHandlingAsync(
|
||||||
|
graphqlClient
|
||||||
|
.mutation<CreateFunctionMutation, CreateFunctionMutationVariables>(
|
||||||
|
gql`
|
||||||
|
mutation CreateFunctionMutation($bucketKey: String!, $appId: String!) {
|
||||||
|
function {
|
||||||
|
createFunction(bucketKey: $bucketKey, appId: $appId) {
|
||||||
|
success
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
bucketKey,
|
||||||
|
appId,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
);
|
||||||
|
return data.function.createFunction.success;
|
||||||
|
},
|
||||||
|
async getStatusAsync(appId: string): Promise<{ status: string; logUrls: string[] }> {
|
||||||
|
const data = await withErrorHandlingAsync(
|
||||||
|
graphqlClient
|
||||||
|
.mutation<GetFunctionStatusMutation, GetFunctionStatusMutationVariables>(
|
||||||
|
gql`
|
||||||
|
mutation GetFunctionStatusMutation($appId: String!) {
|
||||||
|
function {
|
||||||
|
getFunctionStatus(appId: $appId) {
|
||||||
|
status
|
||||||
|
logUrls
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
appId,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.toPromise()
|
||||||
|
);
|
||||||
|
return data.function.getFunctionStatus;
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -7,11 +7,15 @@ import Log from '../log';
|
|||||||
export async function expoCommandAsync(
|
export async function expoCommandAsync(
|
||||||
projectDir: string,
|
projectDir: string,
|
||||||
args: string[],
|
args: string[],
|
||||||
{ silent = false }: { silent?: boolean } = {}
|
{ silent = false, env }: { silent?: boolean; env?: { [key: string]: string } } = {}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const expoCliPath = resolveFrom(projectDir, 'expo/bin/cli.js');
|
const expoCliPath = resolveFrom(projectDir, 'expo/bin/cli.js');
|
||||||
const spawnPromise = spawnAsync(expoCliPath, args, {
|
const spawnPromise = spawnAsync(expoCliPath, args, {
|
||||||
stdio: ['inherit', 'pipe', 'pipe'], // inherit stdin so user can install a missing expo-cli from inside this command
|
stdio: ['inherit', 'pipe', 'pipe'], // inherit stdin so user can install a missing expo-cli from inside this command
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
...env,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const {
|
const {
|
||||||
child: { stdout, stderr },
|
child: { stdout, stderr },
|
||||||
|
|||||||
@@ -78,11 +78,12 @@ export async function makeShallowCopyAsync(src: string, dst: string): Promise<vo
|
|||||||
const ignore = new Ignore(src);
|
const ignore = new Ignore(src);
|
||||||
await ignore.initIgnoreAsync();
|
await ignore.initIgnoreAsync();
|
||||||
await fs.copy(src, dst, {
|
await fs.copy(src, dst, {
|
||||||
filter: (srcFilePath: string) => {
|
filter: (_srcFilePath: string) => {
|
||||||
if (srcFilePath === src) {
|
return true; // TODO: handle ignored build folder
|
||||||
|
/* if (srcFilePath === src) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return !ignore.ignores(path.relative(src, srcFilePath));
|
return !ignore.ignores(path.relative(src, srcFilePath)); */
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
declare module 'pnp-webpack-plugin' {
|
||||||
|
function apply(resolver: any /* EnhancedResolve.Resolver */): void;
|
||||||
|
function moduleLoader(module: NodeModule): any;
|
||||||
|
function forkTsCheckerOptions(config: any): any;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user