diff --git a/.env.example b/.env.example index 944da2aa9296..f398a72aa0af 100644 --- a/.env.example +++ b/.env.example @@ -28,3 +28,7 @@ EXPENSIFY_ACCOUNT_ID_RECEIPTS=-1 EXPENSIFY_ACCOUNT_ID_REWARDS=-1 EXPENSIFY_ACCOUNT_ID_STUDENT_AMBASSADOR=-1 EXPENSIFY_ACCOUNT_ID_SVFG=-1 + +FB_API_KEY=YOUR_API_KEY +FB_APP_ID=YOUR_APP_ID +FB_PROJECT_ID=YOUR_PROJECT_ID diff --git a/.env.production b/.env.production index 5e676134d681..bb925eb70d39 100644 --- a/.env.production +++ b/.env.production @@ -7,3 +7,7 @@ PUSHER_APP_KEY=268df511a204fbb60884 USE_WEB_PROXY=false ENVIRONMENT=production SEND_CRASH_REPORTS=true + +FB_API_KEY=AIzaSyDxzigVLZl4G8MP7jACQ0qpmADMzmrrON0 +FB_APP_ID=1:921154746561:web:1583e882584cf151027c40 +FB_PROJECT_ID=expensify-chat diff --git a/.eslintignore b/.eslintignore index 3d966d096add..5c4b73d8a682 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,6 +3,7 @@ .github/actions/**/index.js *.config.js **/.eslintrc.js +**/.eslintrc.changed.js **/node_modules/** **/dist/** android/**/build/** diff --git a/.eslintrc.changed.js b/.eslintrc.changed.js new file mode 100644 index 000000000000..c279c3e67a51 --- /dev/null +++ b/.eslintrc.changed.js @@ -0,0 +1,10 @@ +module.exports = { + plugins: ['@typescript-eslint', 'deprecation'], + parser: '@typescript-eslint/parser', + parserOptions: { + project: './tsconfig.json', + }, + rules: { + 'deprecation/deprecation': 'error', + }, +}; diff --git a/.eslintrc.js b/.eslintrc.js index cb1219533278..761a62b8314b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -108,7 +108,7 @@ module.exports = { 'plugin:you-dont-need-lodash-underscore/all', 'plugin:prettier/recommended', ], - plugins: ['@typescript-eslint', 'jsdoc', 'you-dont-need-lodash-underscore', 'react-native-a11y', 'react', 'testing-library', 'eslint-plugin-react-compiler'], + plugins: ['@typescript-eslint', 'jsdoc', 'you-dont-need-lodash-underscore', 'react-native-a11y', 'react', 'testing-library', 'eslint-plugin-react-compiler', 'lodash', 'deprecation'], ignorePatterns: ['lib/**'], parser: '@typescript-eslint/parser', parserOptions: { @@ -177,6 +177,7 @@ module.exports = { // ESLint core rules 'es/no-nullish-coalescing-operators': 'off', 'es/no-optional-chaining': 'off', + 'deprecation/deprecation': 'off', // Import specific rules 'import/consistent-type-specifier-style': ['error', 'prefer-top-level'], @@ -231,6 +232,7 @@ module.exports = { 'you-dont-need-lodash-underscore/throttle': 'off', // The suggested alternative (structuredClone) is not supported in Hermes:https://github.com/facebook/hermes/issues/684 'you-dont-need-lodash-underscore/clone-deep': 'off', + 'lodash/import-scope': ['error', 'method'], 'prefer-regex-literals': 'off', 'valid-jsdoc': 'off', 'jsdoc/no-types': 'error', diff --git a/.github/actions/composite/setupNode/action.yml b/.github/actions/composite/setupNode/action.yml index b3bbf7a537ff..8eb4f6474638 100644 --- a/.github/actions/composite/setupNode/action.yml +++ b/.github/actions/composite/setupNode/action.yml @@ -9,12 +9,16 @@ outputs: runs: using: composite steps: + - name: Remove E/App version from package-lock.json + shell: bash + run: jq 'del(.version, .packages[""].version)' package-lock.json > normalized-package-lock.json + - uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' cache: npm cache-dependency-path: | - package-lock.json + normalized-package-lock.json desktop/package-lock.json - id: cache-node-modules diff --git a/.github/actions/javascript/authorChecklist/index.js b/.github/actions/javascript/authorChecklist/index.js index 612a2457d630..e09b95d572ff 100644 --- a/.github/actions/javascript/authorChecklist/index.js +++ b/.github/actions/javascript/authorChecklist/index.js @@ -18153,12 +18153,6 @@ class Buffer { if (!this._map) return; this._normalizePosition(prop, loc, columnOffset); } - withSource(prop, loc, cb) { - if (this._map) { - this.source(prop, loc); - } - cb(); - } _normalizePosition(prop, loc, columnOffset) { const pos = loc[prop]; const target = this._sourcePosition; @@ -18218,9 +18212,9 @@ exports.Placeholder = Placeholder; exports.Program = Program; function File(node) { if (node.program) { - this.print(node.program.interpreter, node); + this.print(node.program.interpreter); } - this.print(node.program, node); + this.print(node.program); } function Program(node) { var _node$directives; @@ -18230,14 +18224,14 @@ function Program(node) { if (directivesLen) { var _node$directives$trai; const newline = node.body.length ? 2 : 1; - this.printSequence(node.directives, node, { + this.printSequence(node.directives, { trailingCommentsLineOffset: newline }); if (!((_node$directives$trai = node.directives[directivesLen - 1].trailingComments) != null && _node$directives$trai.length)) { this.newline(newline); } } - this.printSequence(node.body, node); + this.printSequence(node.body); } function BlockStatement(node) { var _node$directives2; @@ -18246,7 +18240,7 @@ function BlockStatement(node) { if (directivesLen) { var _node$directives$trai2; const newline = node.body.length ? 2 : 1; - this.printSequence(node.directives, node, { + this.printSequence(node.directives, { indent: true, trailingCommentsLineOffset: newline }); @@ -18254,13 +18248,15 @@ function BlockStatement(node) { this.newline(newline); } } - this.printSequence(node.body, node, { + const exit = this.enterForStatementInit(false); + this.printSequence(node.body, { indent: true }); + exit(); this.rightBrace(node); } function Directive(node) { - this.print(node.value, node); + this.print(node.value); this.semicolon(); } const unescapedSingleQuoteRE = /(?:^|[^\\])(?:\\\\)*'/; @@ -18326,7 +18322,7 @@ const { function ClassDeclaration(node, parent) { const inExport = isExportDefaultDeclaration(parent) || isExportNamedDeclaration(parent); if (!inExport || !this._shouldPrintDecoratorsBeforeExport(parent)) { - this.printJoin(node.decorators, node); + this.printJoin(node.decorators); } if (node.declare) { this.word("declare"); @@ -18339,24 +18335,24 @@ function ClassDeclaration(node, parent) { this.word("class"); if (node.id) { this.space(); - this.print(node.id, node); + this.print(node.id); } - this.print(node.typeParameters, node); + this.print(node.typeParameters); if (node.superClass) { this.space(); this.word("extends"); this.space(); - this.print(node.superClass, node); - this.print(node.superTypeParameters, node); + this.print(node.superClass); + this.print(node.superTypeParameters); } if (node.implements) { this.space(); this.word("implements"); this.space(); - this.printList(node.implements, node); + this.printList(node.implements); } this.space(); - this.print(node.body, node); + this.print(node.body); } function ClassBody(node) { this.tokenChar(123); @@ -18364,26 +18360,28 @@ function ClassBody(node) { this.tokenChar(125); } else { this.newline(); - this.printSequence(node.body, node, { + const exit = this.enterForStatementInit(false); + this.printSequence(node.body, { indent: true }); + exit(); if (!this.endsWith(10)) this.newline(); this.rightBrace(node); } } function ClassProperty(node) { var _node$key$loc; - this.printJoin(node.decorators, node); + this.printJoin(node.decorators); const endLine = (_node$key$loc = node.key.loc) == null || (_node$key$loc = _node$key$loc.end) == null ? void 0 : _node$key$loc.line; if (endLine) this.catchUp(endLine); this.tsPrintClassMemberModifiers(node); if (node.computed) { this.tokenChar(91); - this.print(node.key, node); + this.print(node.key); this.tokenChar(93); } else { this._variance(node); - this.print(node.key, node); + this.print(node.key); } if (node.optional) { this.tokenChar(63); @@ -18391,18 +18389,18 @@ function ClassProperty(node) { if (node.definite) { this.tokenChar(33); } - this.print(node.typeAnnotation, node); + this.print(node.typeAnnotation); if (node.value) { this.space(); this.tokenChar(61); this.space(); - this.print(node.value, node); + this.print(node.value); } this.semicolon(); } function ClassAccessorProperty(node) { var _node$key$loc2; - this.printJoin(node.decorators, node); + this.printJoin(node.decorators); const endLine = (_node$key$loc2 = node.key.loc) == null || (_node$key$loc2 = _node$key$loc2.end) == null ? void 0 : _node$key$loc2.line; if (endLine) this.catchUp(endLine); this.tsPrintClassMemberModifiers(node); @@ -18410,11 +18408,11 @@ function ClassAccessorProperty(node) { this.space(); if (node.computed) { this.tokenChar(91); - this.print(node.key, node); + this.print(node.key); this.tokenChar(93); } else { this._variance(node); - this.print(node.key, node); + this.print(node.key); } if (node.optional) { this.tokenChar(63); @@ -18422,44 +18420,44 @@ function ClassAccessorProperty(node) { if (node.definite) { this.tokenChar(33); } - this.print(node.typeAnnotation, node); + this.print(node.typeAnnotation); if (node.value) { this.space(); this.tokenChar(61); this.space(); - this.print(node.value, node); + this.print(node.value); } this.semicolon(); } function ClassPrivateProperty(node) { - this.printJoin(node.decorators, node); + this.printJoin(node.decorators); if (node.static) { this.word("static"); this.space(); } - this.print(node.key, node); - this.print(node.typeAnnotation, node); + this.print(node.key); + this.print(node.typeAnnotation); if (node.value) { this.space(); this.tokenChar(61); this.space(); - this.print(node.value, node); + this.print(node.value); } this.semicolon(); } function ClassMethod(node) { this._classMethodHead(node); this.space(); - this.print(node.body, node); + this.print(node.body); } function ClassPrivateMethod(node) { this._classMethodHead(node); this.space(); - this.print(node.body, node); + this.print(node.body); } function _classMethodHead(node) { var _node$key$loc3; - this.printJoin(node.decorators, node); + this.printJoin(node.decorators); const endLine = (_node$key$loc3 = node.key.loc) == null || (_node$key$loc3 = _node$key$loc3.end) == null ? void 0 : _node$key$loc3.line; if (endLine) this.catchUp(endLine); this.tsPrintClassMemberModifiers(node); @@ -18473,7 +18471,7 @@ function StaticBlock(node) { this.tokenChar(125); } else { this.newline(); - this.printSequence(node.body, node, { + this.printSequence(node.body, { indent: true }); this.rightBrace(node); @@ -18522,7 +18520,7 @@ exports.V8IntrinsicIdentifier = V8IntrinsicIdentifier; exports.YieldExpression = YieldExpression; exports._shouldPrintDecoratorsBeforeExport = _shouldPrintDecoratorsBeforeExport; var _t = __nccwpck_require__(7912); -var n = __nccwpck_require__(9223); +var _index = __nccwpck_require__(9223); const { isCallExpression, isLiteral, @@ -18539,7 +18537,7 @@ function UnaryExpression(node) { } else { this.token(operator); } - this.print(node.argument, node); + this.print(node.argument); } function DoExpression(node) { if (node.async) { @@ -18548,53 +18546,55 @@ function DoExpression(node) { } this.word("do"); this.space(); - this.print(node.body, node); + this.print(node.body); } function ParenthesizedExpression(node) { this.tokenChar(40); - this.print(node.expression, node); + this.print(node.expression); this.rightParens(node); } function UpdateExpression(node) { if (node.prefix) { this.token(node.operator); - this.print(node.argument, node); + this.print(node.argument); } else { - this.printTerminatorless(node.argument, node, true); + this.printTerminatorless(node.argument, true); this.token(node.operator); } } function ConditionalExpression(node) { - this.print(node.test, node); + this.print(node.test); this.space(); this.tokenChar(63); this.space(); - this.print(node.consequent, node); + this.print(node.consequent); this.space(); this.tokenChar(58); this.space(); - this.print(node.alternate, node); + this.print(node.alternate); } function NewExpression(node, parent) { this.word("new"); this.space(); - this.print(node.callee, node); + this.print(node.callee); if (this.format.minified && node.arguments.length === 0 && !node.optional && !isCallExpression(parent, { callee: node }) && !isMemberExpression(parent) && !isNewExpression(parent)) { return; } - this.print(node.typeArguments, node); - this.print(node.typeParameters, node); + this.print(node.typeArguments); + this.print(node.typeParameters); if (node.optional) { this.token("?."); } this.tokenChar(40); - this.printList(node.arguments, node); + const exit = this.enterForStatementInit(false); + this.printList(node.arguments); + exit(); this.rightParens(node); } function SequenceExpression(node) { - this.printList(node.expressions, node); + this.printList(node.expressions); } function ThisExpression() { this.word("this"); @@ -18602,22 +18602,6 @@ function ThisExpression() { function Super() { this.word("super"); } -function isDecoratorMemberExpression(node) { - switch (node.type) { - case "Identifier": - return true; - case "MemberExpression": - return !node.computed && node.property.type === "Identifier" && isDecoratorMemberExpression(node.object); - default: - return false; - } -} -function shouldParenthesizeDecoratorExpression(node) { - if (node.type === "ParenthesizedExpression") { - return false; - } - return !isDecoratorMemberExpression(node.type === "CallExpression" ? node.callee : node); -} function _shouldPrintDecoratorsBeforeExport(node) { if (typeof this.format.decoratorsBeforeExport === "boolean") { return this.format.decoratorsBeforeExport; @@ -18626,16 +18610,7 @@ function _shouldPrintDecoratorsBeforeExport(node) { } function Decorator(node) { this.tokenChar(64); - const { - expression - } = node; - if (shouldParenthesizeDecoratorExpression(expression)) { - this.tokenChar(40); - this.print(expression, node); - this.tokenChar(41); - } else { - this.print(expression, node); - } + this.print(node.expression); this.newline(); } function OptionalMemberExpression(node) { @@ -18646,7 +18621,7 @@ function OptionalMemberExpression(node) { optional, property } = node; - this.print(node.object, node); + this.print(node.object); if (!computed && isMemberExpression(property)) { throw new TypeError("Got a MemberExpression for MemberExpression property"); } @@ -18658,32 +18633,36 @@ function OptionalMemberExpression(node) { } if (computed) { this.tokenChar(91); - this.print(property, node); + this.print(property); this.tokenChar(93); } else { if (!optional) { this.tokenChar(46); } - this.print(property, node); + this.print(property); } } function OptionalCallExpression(node) { - this.print(node.callee, node); - this.print(node.typeParameters, node); + this.print(node.callee); + this.print(node.typeParameters); if (node.optional) { this.token("?."); } - this.print(node.typeArguments, node); + this.print(node.typeArguments); this.tokenChar(40); - this.printList(node.arguments, node); + const exit = this.enterForStatementInit(false); + this.printList(node.arguments); + exit(); this.rightParens(node); } function CallExpression(node) { - this.print(node.callee, node); - this.print(node.typeArguments, node); - this.print(node.typeParameters, node); + this.print(node.callee); + this.print(node.typeArguments); + this.print(node.typeParameters); this.tokenChar(40); - this.printList(node.arguments, node); + const exit = this.enterForStatementInit(false); + this.printList(node.arguments); + exit(); this.rightParens(node); } function Import() { @@ -18693,7 +18672,7 @@ function AwaitExpression(node) { this.word("await"); if (node.argument) { this.space(); - this.printTerminatorless(node.argument, node, false); + this.printTerminatorless(node.argument, false); } } function YieldExpression(node) { @@ -18702,12 +18681,12 @@ function YieldExpression(node) { this.tokenChar(42); if (node.argument) { this.space(); - this.print(node.argument, node); + this.print(node.argument); } } else { if (node.argument) { this.space(); - this.printTerminatorless(node.argument, node, false); + this.printTerminatorless(node.argument, false); } } } @@ -18715,43 +18694,40 @@ function EmptyStatement() { this.semicolon(true); } function ExpressionStatement(node) { - this.print(node.expression, node); + this.tokenContext |= _index.TokenContext.expressionStatement; + this.print(node.expression); this.semicolon(); } function AssignmentPattern(node) { - this.print(node.left, node); - if (node.left.optional) this.tokenChar(63); - this.print(node.left.typeAnnotation, node); + this.print(node.left); + if (node.left.type === "Identifier") { + if (node.left.optional) this.tokenChar(63); + this.print(node.left.typeAnnotation); + } this.space(); this.tokenChar(61); this.space(); - this.print(node.right, node); + this.print(node.right); } -function AssignmentExpression(node, parent) { - const parens = this.inForStatementInitCounter && node.operator === "in" && !n.needsParens(node, parent); - if (parens) { - this.tokenChar(40); - } - this.print(node.left, node); +function AssignmentExpression(node) { + this.print(node.left); this.space(); if (node.operator === "in" || node.operator === "instanceof") { this.word(node.operator); } else { this.token(node.operator); + this._endsWithDiv = node.operator === "/"; } this.space(); - this.print(node.right, node); - if (parens) { - this.tokenChar(41); - } + this.print(node.right); } function BindExpression(node) { - this.print(node.object, node); + this.print(node.object); this.token("::"); - this.print(node.callee, node); + this.print(node.callee); } function MemberExpression(node) { - this.print(node.object, node); + this.print(node.object); if (!node.computed && isMemberExpression(node.property)) { throw new TypeError("Got a MemberExpression for MemberExpression property"); } @@ -18760,22 +18736,24 @@ function MemberExpression(node) { computed = true; } if (computed) { + const exit = this.enterForStatementInit(false); this.tokenChar(91); - this.print(node.property, node); + this.print(node.property); this.tokenChar(93); + exit(); } else { this.tokenChar(46); - this.print(node.property, node); + this.print(node.property); } } function MetaProperty(node) { - this.print(node.meta, node); + this.print(node.meta); this.tokenChar(46); - this.print(node.property, node); + this.print(node.property); } function PrivateName(node) { this.tokenChar(35); - this.print(node.id, node); + this.print(node.id); } function V8IntrinsicIdentifier(node) { this.tokenChar(37); @@ -18792,7 +18770,7 @@ function ModuleExpression(node) { if (body.body.length || body.directives.length) { this.newline(); } - this.print(body, node); + this.print(body); this.dedent(); this.rightBrace(node); } @@ -18887,6 +18865,7 @@ exports._interfaceish = _interfaceish; exports._variance = _variance; var _t = __nccwpck_require__(7912); var _modules = __nccwpck_require__(319); +var _index = __nccwpck_require__(9223); var _types2 = __nccwpck_require__(3817); const { isDeclareExportDeclaration, @@ -18896,7 +18875,7 @@ function AnyTypeAnnotation() { this.word("any"); } function ArrayTypeAnnotation(node) { - this.print(node.elementType, node, true); + this.print(node.elementType, true); this.tokenChar(91); this.tokenChar(93); } @@ -18925,11 +18904,11 @@ function DeclareFunction(node, parent) { } this.word("function"); this.space(); - this.print(node.id, node); - this.print(node.id.typeAnnotation.typeAnnotation, node); + this.print(node.id); + this.print(node.id.typeAnnotation.typeAnnotation); if (node.predicate) { this.space(); - this.print(node.predicate, node); + this.print(node.predicate); } this.semicolon(); } @@ -18941,7 +18920,7 @@ function DeclaredPredicate(node) { this.tokenChar(37); this.word("checks"); this.tokenChar(40); - this.print(node.value, node); + this.print(node.value); this.tokenChar(41); } function DeclareInterface(node) { @@ -18954,9 +18933,9 @@ function DeclareModule(node) { this.space(); this.word("module"); this.space(); - this.print(node.id, node); + this.print(node.id); this.space(); - this.print(node.body, node); + this.print(node.body); } function DeclareModuleExports(node) { this.word("declare"); @@ -18964,7 +18943,7 @@ function DeclareModuleExports(node) { this.word("module"); this.tokenChar(46); this.word("exports"); - this.print(node.typeAnnotation, node); + this.print(node.typeAnnotation); } function DeclareTypeAlias(node) { this.word("declare"); @@ -18985,8 +18964,8 @@ function DeclareVariable(node, parent) { } this.word("var"); this.space(); - this.print(node.id, node); - this.print(node.id.typeAnnotation, node); + this.print(node.id); + this.print(node.id.typeAnnotation); this.semicolon(); } function DeclareExportDeclaration(node) { @@ -19012,8 +18991,8 @@ function EnumDeclaration(node) { } = node; this.word("enum"); this.space(); - this.print(id, node); - this.print(body, node); + this.print(id); + this.print(body); } function enumExplicitType(context, name, hasExplicitType) { if (hasExplicitType) { @@ -19032,7 +19011,7 @@ function enumBody(context, node) { context.indent(); context.newline(); for (const member of members) { - context.print(member, node); + context.print(member); context.newline(); } if (node.hasUnknownMembers) { @@ -19071,19 +19050,15 @@ function EnumDefaultedMember(node) { const { id } = node; - this.print(id, node); + this.print(id); this.tokenChar(44); } function enumInitializedMember(context, node) { - const { - id, - init - } = node; - context.print(id, node); + context.print(node.id); context.space(); context.token("="); context.space(); - context.print(init, node); + context.print(node.init); context.token(","); } function EnumBooleanMember(node) { @@ -19098,13 +19073,13 @@ function EnumStringMember(node) { function FlowExportDeclaration(node) { if (node.declaration) { const declar = node.declaration; - this.print(declar, node); + this.print(declar); if (!isStatement(declar)) this.semicolon(); } else { this.tokenChar(123); if (node.specifiers.length) { this.space(); - this.printList(node.specifiers, node); + this.printList(node.specifiers); this.space(); } this.tokenChar(125); @@ -19112,7 +19087,7 @@ function FlowExportDeclaration(node) { this.space(); this.word("from"); this.space(); - this.print(node.source, node); + this.print(node.source); } this.semicolon(); } @@ -19121,26 +19096,26 @@ function ExistsTypeAnnotation() { this.tokenChar(42); } function FunctionTypeAnnotation(node, parent) { - this.print(node.typeParameters, node); + this.print(node.typeParameters); this.tokenChar(40); if (node.this) { this.word("this"); this.tokenChar(58); this.space(); - this.print(node.this.typeAnnotation, node); + this.print(node.this.typeAnnotation); if (node.params.length || node.rest) { this.tokenChar(44); this.space(); } } - this.printList(node.params, node); + this.printList(node.params); if (node.rest) { if (node.params.length) { this.tokenChar(44); this.space(); } this.token("..."); - this.print(node.rest, node); + this.print(node.rest); } this.tokenChar(41); const type = parent == null ? void 0 : parent.type; @@ -19151,30 +19126,30 @@ function FunctionTypeAnnotation(node, parent) { this.token("=>"); } this.space(); - this.print(node.returnType, node); + this.print(node.returnType); } function FunctionTypeParam(node) { - this.print(node.name, node); + this.print(node.name); if (node.optional) this.tokenChar(63); if (node.name) { this.tokenChar(58); this.space(); } - this.print(node.typeAnnotation, node); + this.print(node.typeAnnotation); } function InterfaceExtends(node) { - this.print(node.id, node); - this.print(node.typeParameters, node, true); + this.print(node.id); + this.print(node.typeParameters, true); } function _interfaceish(node) { var _node$extends; - this.print(node.id, node); - this.print(node.typeParameters, node); + this.print(node.id); + this.print(node.typeParameters); if ((_node$extends = node.extends) != null && _node$extends.length) { this.space(); this.word("extends"); this.space(); - this.printList(node.extends, node); + this.printList(node.extends); } if (node.type === "DeclareClass") { var _node$mixins, _node$implements; @@ -19182,17 +19157,17 @@ function _interfaceish(node) { this.space(); this.word("mixins"); this.space(); - this.printList(node.mixins, node); + this.printList(node.mixins); } if ((_node$implements = node.implements) != null && _node$implements.length) { this.space(); this.word("implements"); this.space(); - this.printList(node.implements, node); + this.printList(node.implements); } } this.space(); - this.print(node.body, node); + this.print(node.body); } function _variance(node) { var _node$variance; @@ -19222,13 +19197,13 @@ function InterfaceTypeAnnotation(node) { this.space(); this.word("extends"); this.space(); - this.printList(node.extends, node); + this.printList(node.extends); } this.space(); - this.print(node.body, node); + this.print(node.body); } function IntersectionTypeAnnotation(node) { - this.printJoin(node.types, node, { + this.printJoin(node.types, { separator: andSeparator }); } @@ -19240,7 +19215,7 @@ function EmptyTypeAnnotation() { } function NullableTypeAnnotation(node) { this.tokenChar(63); - this.print(node.typeAnnotation, node); + this.print(node.typeAnnotation); } function NumberTypeAnnotation() { this.word("number"); @@ -19253,47 +19228,51 @@ function ThisTypeAnnotation() { } function TupleTypeAnnotation(node) { this.tokenChar(91); - this.printList(node.types, node); + this.printList(node.types); this.tokenChar(93); } function TypeofTypeAnnotation(node) { this.word("typeof"); this.space(); - this.print(node.argument, node); + this.print(node.argument); } function TypeAlias(node) { this.word("type"); this.space(); - this.print(node.id, node); - this.print(node.typeParameters, node); + this.print(node.id); + this.print(node.typeParameters); this.space(); this.tokenChar(61); this.space(); - this.print(node.right, node); + this.print(node.right); this.semicolon(); } -function TypeAnnotation(node) { +function TypeAnnotation(node, parent) { this.tokenChar(58); this.space(); - if (node.optional) this.tokenChar(63); - this.print(node.typeAnnotation, node); + if (parent.type === "ArrowFunctionExpression") { + this.tokenContext |= _index.TokenContext.arrowFlowReturnType; + } else if (node.optional) { + this.tokenChar(63); + } + this.print(node.typeAnnotation); } function TypeParameterInstantiation(node) { this.tokenChar(60); - this.printList(node.params, node, {}); + this.printList(node.params, {}); this.tokenChar(62); } function TypeParameter(node) { this._variance(node); this.word(node.name); if (node.bound) { - this.print(node.bound, node); + this.print(node.bound); } if (node.default) { this.space(); this.tokenChar(61); this.space(); - this.print(node.default, node); + this.print(node.default); } } function OpaqueType(node) { @@ -19301,18 +19280,18 @@ function OpaqueType(node) { this.space(); this.word("type"); this.space(); - this.print(node.id, node); - this.print(node.typeParameters, node); + this.print(node.id); + this.print(node.typeParameters); if (node.supertype) { this.tokenChar(58); this.space(); - this.print(node.supertype, node); + this.print(node.supertype); } if (node.impltype) { this.space(); this.tokenChar(61); this.space(); - this.print(node.impltype, node); + this.print(node.impltype); } this.semicolon(); } @@ -19326,7 +19305,7 @@ function ObjectTypeAnnotation(node) { if (props.length) { this.newline(); this.space(); - this.printJoin(props, node, { + this.printJoin(props, { addNewlines(leading) { if (leading && !props[0]) return 1; }, @@ -19362,7 +19341,7 @@ function ObjectTypeInternalSlot(node) { } this.tokenChar(91); this.tokenChar(91); - this.print(node.id, node); + this.print(node.id); this.tokenChar(93); this.tokenChar(93); if (node.optional) this.tokenChar(63); @@ -19370,14 +19349,14 @@ function ObjectTypeInternalSlot(node) { this.tokenChar(58); this.space(); } - this.print(node.value, node); + this.print(node.value); } function ObjectTypeCallProperty(node) { if (node.static) { this.word("static"); this.space(); } - this.print(node.value, node); + this.print(node.value); } function ObjectTypeIndexer(node) { if (node.static) { @@ -19387,15 +19366,15 @@ function ObjectTypeIndexer(node) { this._variance(node); this.tokenChar(91); if (node.id) { - this.print(node.id, node); + this.print(node.id); this.tokenChar(58); this.space(); } - this.print(node.key, node); + this.print(node.key); this.tokenChar(93); this.tokenChar(58); this.space(); - this.print(node.value, node); + this.print(node.value); } function ObjectTypeProperty(node) { if (node.proto) { @@ -19411,22 +19390,22 @@ function ObjectTypeProperty(node) { this.space(); } this._variance(node); - this.print(node.key, node); + this.print(node.key); if (node.optional) this.tokenChar(63); if (!node.method) { this.tokenChar(58); this.space(); } - this.print(node.value, node); + this.print(node.value); } function ObjectTypeSpreadProperty(node) { this.token("..."); - this.print(node.argument, node); + this.print(node.argument); } function QualifiedTypeIdentifier(node) { - this.print(node.qualification, node); + this.print(node.qualification); this.tokenChar(46); - this.print(node.id, node); + this.print(node.id); } function SymbolTypeAnnotation() { this.word("symbol"); @@ -19437,14 +19416,14 @@ function orSeparator() { this.space(); } function UnionTypeAnnotation(node) { - this.printJoin(node.types, node, { + this.printJoin(node.types, { separator: orSeparator }); } function TypeCastExpression(node) { this.tokenChar(40); - this.print(node.expression, node); - this.print(node.typeAnnotation, node); + this.print(node.expression); + this.print(node.typeAnnotation); this.tokenChar(41); } function Variance(node) { @@ -19458,18 +19437,18 @@ function VoidTypeAnnotation() { this.word("void"); } function IndexedAccessType(node) { - this.print(node.objectType, node, true); + this.print(node.objectType, true); this.tokenChar(91); - this.print(node.indexType, node); + this.print(node.indexType); this.tokenChar(93); } function OptionalIndexedAccessType(node) { - this.print(node.objectType, node); + this.print(node.objectType); if (node.optional) { this.token("?."); } this.tokenChar(91); - this.print(node.indexType, node); + this.print(node.indexType); this.tokenChar(93); } @@ -19639,41 +19618,41 @@ exports.JSXSpreadAttribute = JSXSpreadAttribute; exports.JSXSpreadChild = JSXSpreadChild; exports.JSXText = JSXText; function JSXAttribute(node) { - this.print(node.name, node); + this.print(node.name); if (node.value) { this.tokenChar(61); - this.print(node.value, node); + this.print(node.value); } } function JSXIdentifier(node) { this.word(node.name); } function JSXNamespacedName(node) { - this.print(node.namespace, node); + this.print(node.namespace); this.tokenChar(58); - this.print(node.name, node); + this.print(node.name); } function JSXMemberExpression(node) { - this.print(node.object, node); + this.print(node.object); this.tokenChar(46); - this.print(node.property, node); + this.print(node.property); } function JSXSpreadAttribute(node) { this.tokenChar(123); this.token("..."); - this.print(node.argument, node); - this.tokenChar(125); + this.print(node.argument); + this.rightBrace(node); } function JSXExpressionContainer(node) { this.tokenChar(123); - this.print(node.expression, node); - this.tokenChar(125); + this.print(node.expression); + this.rightBrace(node); } function JSXSpreadChild(node) { this.tokenChar(123); this.token("..."); - this.print(node.expression, node); - this.tokenChar(125); + this.print(node.expression); + this.rightBrace(node); } function JSXText(node) { const raw = this.getPossibleRaw(node); @@ -19685,25 +19664,25 @@ function JSXText(node) { } function JSXElement(node) { const open = node.openingElement; - this.print(open, node); + this.print(open); if (open.selfClosing) return; this.indent(); for (const child of node.children) { - this.print(child, node); + this.print(child); } this.dedent(); - this.print(node.closingElement, node); + this.print(node.closingElement); } function spaceSeparator() { this.space(); } function JSXOpeningElement(node) { this.tokenChar(60); - this.print(node.name, node); - this.print(node.typeParameters, node); + this.print(node.name); + this.print(node.typeParameters); if (node.attributes.length > 0) { this.space(); - this.printJoin(node.attributes, node, { + this.printJoin(node.attributes, { separator: spaceSeparator }); } @@ -19716,20 +19695,20 @@ function JSXOpeningElement(node) { } function JSXClosingElement(node) { this.token(""); this.space(); - this.print(node.body, node); + this.tokenContext |= _index.TokenContext.arrowBody; + this.print(node.body); } function hasTypesOrComments(node, param) { var _param$leadingComment, _param$trailingCommen; @@ -19949,6 +19932,7 @@ exports.ImportNamespaceSpecifier = ImportNamespaceSpecifier; exports.ImportSpecifier = ImportSpecifier; exports._printAttributes = _printAttributes; var _t = __nccwpck_require__(7912); +var _index = __nccwpck_require__(9223); const { isClassDeclaration, isExportDefaultSpecifier, @@ -19962,31 +19946,31 @@ function ImportSpecifier(node) { this.word(node.importKind); this.space(); } - this.print(node.imported, node); + this.print(node.imported); if (node.local && node.local.name !== node.imported.name) { this.space(); this.word("as"); this.space(); - this.print(node.local, node); + this.print(node.local); } } function ImportDefaultSpecifier(node) { - this.print(node.local, node); + this.print(node.local); } function ExportDefaultSpecifier(node) { - this.print(node.exported, node); + this.print(node.exported); } function ExportSpecifier(node) { if (node.exportKind === "type") { this.word("type"); this.space(); } - this.print(node.local, node); + this.print(node.local); if (node.exported && node.local.name !== node.exported.name) { this.space(); this.word("as"); this.space(); - this.print(node.exported, node); + this.print(node.exported); } } function ExportNamespaceSpecifier(node) { @@ -19994,7 +19978,7 @@ function ExportNamespaceSpecifier(node) { this.space(); this.word("as"); this.space(); - this.print(node.exported, node); + this.print(node.exported); } let warningShown = false; function _printAttributes(node) { @@ -20019,12 +20003,12 @@ Please specify the "importAttributesKeyword" generator option, whose value can b this.word(useAssertKeyword ? "assert" : "with"); this.space(); if (!useAssertKeyword && importAttributesKeyword !== "with") { - this.printList(attributes || assertions, node); + this.printList(attributes || assertions); return; } this.tokenChar(123); this.space(); - this.printList(attributes || assertions, node); + this.printList(attributes || assertions); this.space(); this.tokenChar(125); } @@ -20041,17 +20025,17 @@ function ExportAllDeclaration(node) { this.word("from"); this.space(); if ((_node$attributes = node.attributes) != null && _node$attributes.length || (_node$assertions = node.assertions) != null && _node$assertions.length) { - this.print(node.source, node, true); + this.print(node.source, true); this.space(); this._printAttributes(node); } else { - this.print(node.source, node); + this.print(node.source); } this.semicolon(); } function maybePrintDecoratorsBeforeExport(printer, node) { if (isClassDeclaration(node.declaration) && printer._shouldPrintDecoratorsBeforeExport(node)) { - printer.printJoin(node.declaration.decorators, node); + printer.printJoin(node.declaration.decorators); } } function ExportNamedDeclaration(node) { @@ -20060,7 +20044,7 @@ function ExportNamedDeclaration(node) { this.space(); if (node.declaration) { const declar = node.declaration; - this.print(declar, node); + this.print(declar); if (!isStatement(declar)) this.semicolon(); } else { if (node.exportKind === "type") { @@ -20073,7 +20057,7 @@ function ExportNamedDeclaration(node) { const first = specifiers[0]; if (isExportDefaultSpecifier(first) || isExportNamespaceSpecifier(first)) { hasSpecial = true; - this.print(specifiers.shift(), node); + this.print(specifiers.shift()); if (specifiers.length) { this.tokenChar(44); this.space(); @@ -20086,7 +20070,7 @@ function ExportNamedDeclaration(node) { this.tokenChar(123); if (specifiers.length) { this.space(); - this.printList(specifiers, node); + this.printList(specifiers); this.space(); } this.tokenChar(125); @@ -20097,11 +20081,11 @@ function ExportNamedDeclaration(node) { this.word("from"); this.space(); if ((_node$attributes2 = node.attributes) != null && _node$attributes2.length || (_node$assertions2 = node.assertions) != null && _node$assertions2.length) { - this.print(node.source, node, true); + this.print(node.source, true); this.space(); this._printAttributes(node); } else { - this.print(node.source, node); + this.print(node.source); } } this.semicolon(); @@ -20114,8 +20098,9 @@ function ExportDefaultDeclaration(node) { this.space(); this.word("default"); this.space(); + this.tokenContext |= _index.TokenContext.exportDefault; const declar = node.declaration; - this.print(declar, node); + this.print(declar); if (!isStatement(declar)) this.semicolon(); } function ImportDeclaration(node) { @@ -20141,7 +20126,7 @@ function ImportDeclaration(node) { while (hasSpecifiers) { const first = specifiers[0]; if (isImportDefaultSpecifier(first) || isImportNamespaceSpecifier(first)) { - this.print(specifiers.shift(), node); + this.print(specifiers.shift()); if (specifiers.length) { this.tokenChar(44); this.space(); @@ -20153,7 +20138,7 @@ function ImportDeclaration(node) { if (specifiers.length) { this.tokenChar(123); this.space(); - this.printList(specifiers, node); + this.printList(specifiers); this.space(); this.tokenChar(125); } else if (isTypeKind && !hasSpecifiers) { @@ -20166,11 +20151,11 @@ function ImportDeclaration(node) { this.space(); } if ((_node$attributes3 = node.attributes) != null && _node$attributes3.length || (_node$assertions3 = node.assertions) != null && _node$assertions3.length) { - this.print(node.source, node, true); + this.print(node.source, true); this.space(); this._printAttributes(node); } else { - this.print(node.source, node); + this.print(node.source); } this.semicolon(); } @@ -20185,7 +20170,7 @@ function ImportNamespaceSpecifier(node) { this.space(); this.word("as"); this.space(); - this.print(node.local, node); + this.print(node.local); } function ImportExpression(node) { this.word("import"); @@ -20194,11 +20179,11 @@ function ImportExpression(node) { this.word(node.phase); } this.tokenChar(40); - this.print(node.source, node); + this.print(node.source); if (node.options != null) { this.tokenChar(44); this.space(); - this.print(node.options, node); + this.print(node.options); } this.tokenChar(41); } @@ -20236,6 +20221,7 @@ exports.VariableDeclarator = VariableDeclarator; exports.WhileStatement = WhileStatement; exports.WithStatement = WithStatement; var _t = __nccwpck_require__(7912); +var _index = __nccwpck_require__(9223); const { isFor, isForStatement, @@ -20246,7 +20232,7 @@ function WithStatement(node) { this.word("with"); this.space(); this.tokenChar(40); - this.print(node.object, node); + this.print(node.object); this.tokenChar(41); this.printBlock(node); } @@ -20254,7 +20240,7 @@ function IfStatement(node) { this.word("if"); this.space(); this.tokenChar(40); - this.print(node.test, node); + this.print(node.test); this.tokenChar(41); this.space(); const needsBlock = node.alternate && isIfStatement(getLastStatement(node.consequent)); @@ -20263,7 +20249,7 @@ function IfStatement(node) { this.newline(); this.indent(); } - this.printAndIndentOnComments(node.consequent, node); + this.printAndIndentOnComments(node.consequent); if (needsBlock) { this.dedent(); this.newline(); @@ -20273,7 +20259,7 @@ function IfStatement(node) { if (this.endsWith(125)) this.space(); this.word("else"); this.space(); - this.printAndIndentOnComments(node.alternate, node); + this.printAndIndentOnComments(node.alternate); } } function getLastStatement(statement) { @@ -20289,18 +20275,21 @@ function ForStatement(node) { this.word("for"); this.space(); this.tokenChar(40); - this.inForStatementInitCounter++; - this.print(node.init, node); - this.inForStatementInitCounter--; + { + const exit = this.enterForStatementInit(true); + this.tokenContext |= _index.TokenContext.forHead; + this.print(node.init); + exit(); + } this.tokenChar(59); if (node.test) { this.space(); - this.print(node.test, node); + this.print(node.test); } this.tokenChar(59); if (node.update) { this.space(); - this.print(node.update, node); + this.print(node.update); } this.tokenChar(41); this.printBlock(node); @@ -20309,7 +20298,7 @@ function WhileStatement(node) { this.word("while"); this.space(); this.tokenChar(40); - this.print(node.test, node); + this.print(node.test); this.tokenChar(41); this.printBlock(node); } @@ -20323,11 +20312,16 @@ function ForXStatement(node) { } this.noIndentInnerCommentsHere(); this.tokenChar(40); - this.print(node.left, node); + { + const exit = isForOf ? null : this.enterForStatementInit(true); + this.tokenContext |= isForOf ? _index.TokenContext.forOfHead : _index.TokenContext.forInHead; + this.print(node.left); + exit == null || exit(); + } this.space(); this.word(isForOf ? "of" : "in"); this.space(); - this.print(node.right, node); + this.print(node.right); this.tokenChar(41); this.printBlock(node); } @@ -20336,59 +20330,59 @@ const ForOfStatement = exports.ForOfStatement = ForXStatement; function DoWhileStatement(node) { this.word("do"); this.space(); - this.print(node.body, node); + this.print(node.body); this.space(); this.word("while"); this.space(); this.tokenChar(40); - this.print(node.test, node); + this.print(node.test); this.tokenChar(41); this.semicolon(); } -function printStatementAfterKeyword(printer, node, parent, isLabel) { +function printStatementAfterKeyword(printer, node, isLabel) { if (node) { printer.space(); - printer.printTerminatorless(node, parent, isLabel); + printer.printTerminatorless(node, isLabel); } printer.semicolon(); } function BreakStatement(node) { this.word("break"); - printStatementAfterKeyword(this, node.label, node, true); + printStatementAfterKeyword(this, node.label, true); } function ContinueStatement(node) { this.word("continue"); - printStatementAfterKeyword(this, node.label, node, true); + printStatementAfterKeyword(this, node.label, true); } function ReturnStatement(node) { this.word("return"); - printStatementAfterKeyword(this, node.argument, node, false); + printStatementAfterKeyword(this, node.argument, false); } function ThrowStatement(node) { this.word("throw"); - printStatementAfterKeyword(this, node.argument, node, false); + printStatementAfterKeyword(this, node.argument, false); } function LabeledStatement(node) { - this.print(node.label, node); + this.print(node.label); this.tokenChar(58); this.space(); - this.print(node.body, node); + this.print(node.body); } function TryStatement(node) { this.word("try"); this.space(); - this.print(node.block, node); + this.print(node.block); this.space(); if (node.handlers) { - this.print(node.handlers[0], node); + this.print(node.handlers[0]); } else { - this.print(node.handler, node); + this.print(node.handler); } if (node.finalizer) { this.space(); this.word("finally"); this.space(); - this.print(node.finalizer, node); + this.print(node.finalizer); } } function CatchClause(node) { @@ -20396,22 +20390,22 @@ function CatchClause(node) { this.space(); if (node.param) { this.tokenChar(40); - this.print(node.param, node); - this.print(node.param.typeAnnotation, node); + this.print(node.param); + this.print(node.param.typeAnnotation); this.tokenChar(41); this.space(); } - this.print(node.body, node); + this.print(node.body); } function SwitchStatement(node) { this.word("switch"); this.space(); this.tokenChar(40); - this.print(node.discriminant, node); + this.print(node.discriminant); this.tokenChar(41); this.space(); this.tokenChar(123); - this.printSequence(node.cases, node, { + this.printSequence(node.cases, { indent: true, addNewlines(leading, cas) { if (!leading && node.cases[node.cases.length - 1] === cas) return -1; @@ -20423,7 +20417,7 @@ function SwitchCase(node) { if (node.test) { this.word("case"); this.space(); - this.print(node.test, node); + this.print(node.test); this.tokenChar(58); } else { this.word("default"); @@ -20431,7 +20425,7 @@ function SwitchCase(node) { } if (node.consequent.length) { this.newline(); - this.printSequence(node.consequent, node, { + this.printSequence(node.consequent, { indent: true }); } @@ -20448,7 +20442,13 @@ function VariableDeclaration(node, parent) { const { kind } = node; - this.word(kind, kind === "using" || kind === "await using"); + if (kind === "await using") { + this.word("await"); + this.space(); + this.word("using", true); + } else { + this.word(kind, kind === "using"); + } this.space(); let hasInits = false; if (!isFor(parent)) { @@ -20458,7 +20458,7 @@ function VariableDeclaration(node, parent) { } } } - this.printList(node.declarations, node, { + this.printList(node.declarations, { separator: hasInits ? function () { this.tokenChar(44); this.newline(); @@ -20475,14 +20475,14 @@ function VariableDeclaration(node, parent) { this.semicolon(); } function VariableDeclarator(node) { - this.print(node.id, node); + this.print(node.id); if (node.definite) this.tokenChar(33); - this.print(node.id.typeAnnotation, node); + this.print(node.id.typeAnnotation); if (node.init) { this.space(); this.tokenChar(61); this.space(); - this.print(node.init, node); + this.print(node.init); } } @@ -20504,9 +20504,9 @@ exports.TaggedTemplateExpression = TaggedTemplateExpression; exports.TemplateElement = TemplateElement; exports.TemplateLiteral = TemplateLiteral; function TaggedTemplateExpression(node) { - this.print(node.tag, node); - this.print(node.typeParameters, node); - this.print(node.quasi, node); + this.print(node.tag); + this.print(node.typeParameters); + this.print(node.quasi); } function TemplateElement() { throw new Error("TemplateElement printing is handled in TemplateLiteral"); @@ -20518,7 +20518,7 @@ function TemplateLiteral(node) { partRaw += quasis[i].value.raw; if (i + 1 < quasis.length) { this.token(partRaw + "${", true); - this.print(node.expressions[i], node); + this.print(node.expressions[i]); partRaw = "}"; } } @@ -20575,62 +20575,66 @@ function ArgumentPlaceholder() { } function RestElement(node) { this.token("..."); - this.print(node.argument, node); + this.print(node.argument); } function ObjectExpression(node) { const props = node.properties; this.tokenChar(123); if (props.length) { + const exit = this.enterForStatementInit(false); this.space(); - this.printList(props, node, { + this.printList(props, { indent: true, statement: true }); this.space(); + exit(); } this.sourceWithOffset("end", node.loc, -1); this.tokenChar(125); } function ObjectMethod(node) { - this.printJoin(node.decorators, node); + this.printJoin(node.decorators); this._methodHead(node); this.space(); - this.print(node.body, node); + this.print(node.body); } function ObjectProperty(node) { - this.printJoin(node.decorators, node); + this.printJoin(node.decorators); if (node.computed) { this.tokenChar(91); - this.print(node.key, node); + this.print(node.key); this.tokenChar(93); } else { if (isAssignmentPattern(node.value) && isIdentifier(node.key) && node.key.name === node.value.left.name) { - this.print(node.value, node); + this.print(node.value); return; } - this.print(node.key, node); + this.print(node.key); if (node.shorthand && isIdentifier(node.key) && isIdentifier(node.value) && node.key.name === node.value.name) { return; } } this.tokenChar(58); this.space(); - this.print(node.value, node); + this.print(node.value); } function ArrayExpression(node) { const elems = node.elements; const len = elems.length; this.tokenChar(91); + const exit = this.enterForStatementInit(false); for (let i = 0; i < elems.length; i++) { const elem = elems[i]; if (elem) { if (i > 0) this.space(); - this.print(elem, node); + this.print(elem); if (i < len - 1) this.tokenChar(44); } else { this.tokenChar(44); } } + exit(); this.tokenChar(93); } function RecordExpression(node) { @@ -20651,7 +20655,7 @@ function RecordExpression(node) { this.token(startToken); if (props.length) { this.space(); - this.printList(props, node, { + this.printList(props, { indent: true, statement: true }); @@ -20680,7 +20684,7 @@ function TupleExpression(node) { const elem = elems[i]; if (elem) { if (i > 0) this.space(); - this.print(elem, node); + this.print(elem); if (i < len - 1) this.tokenChar(44); } } @@ -20749,10 +20753,10 @@ function TopicReference() { } } function PipelineTopicExpression(node) { - this.print(node.expression, node); + this.print(node.expression); } function PipelineBareFunction(node) { - this.print(node.callee, node); + this.print(node.callee); } function PipelinePrimaryTopicReference() { this.tokenChar(35); @@ -20844,11 +20848,11 @@ function TSTypeAnnotation(node) { this.tokenChar(58); this.space(); if (node.optional) this.tokenChar(63); - this.print(node.typeAnnotation, node); + this.print(node.typeAnnotation); } function TSTypeParameterInstantiation(node, parent) { this.tokenChar(60); - this.printList(node.params, node, {}); + this.printList(node.params, {}); if (parent.type === "ArrowFunctionExpression" && node.params.length === 1) { this.tokenChar(44); } @@ -20868,13 +20872,13 @@ function TSTypeParameter(node) { this.space(); this.word("extends"); this.space(); - this.print(node.constraint, node); + this.print(node.constraint); } if (node.default) { this.space(); this.tokenChar(61); this.space(); - this.print(node.default, node); + this.print(node.default); } } function TSParameterProperty(node) { @@ -20894,26 +20898,26 @@ function TSDeclareFunction(node, parent) { this.space(); } this._functionHead(node, parent); - this.tokenChar(59); + this.semicolon(); } function TSDeclareMethod(node) { this._classMethodHead(node); - this.tokenChar(59); + this.semicolon(); } function TSQualifiedName(node) { - this.print(node.left, node); + this.print(node.left); this.tokenChar(46); - this.print(node.right, node); + this.print(node.right); } function TSCallSignatureDeclaration(node) { this.tsPrintSignatureDeclarationBase(node); - this.tokenChar(59); + this.semicolon(); } function TSConstructSignatureDeclaration(node) { this.word("new"); this.space(); this.tsPrintSignatureDeclarationBase(node); - this.tokenChar(59); + this.semicolon(); } function TSPropertySignature(node) { const { @@ -20924,14 +20928,14 @@ function TSPropertySignature(node) { this.space(); } this.tsPrintPropertyOrMethodName(node); - this.print(node.typeAnnotation, node); - this.tokenChar(59); + this.print(node.typeAnnotation); + this.semicolon(); } function tsPrintPropertyOrMethodName(node) { if (node.computed) { this.tokenChar(91); } - this.print(node.key, node); + this.print(node.key); if (node.computed) { this.tokenChar(93); } @@ -20949,7 +20953,7 @@ function TSMethodSignature(node) { } this.tsPrintPropertyOrMethodName(node); this.tsPrintSignatureDeclarationBase(node); - this.tokenChar(59); + this.semicolon(); } function TSIndexSignature(node) { const { @@ -20965,10 +20969,10 @@ function TSIndexSignature(node) { this.space(); } this.tokenChar(91); - this._parameters(node.parameters, node); + this._parameters(node.parameters); this.tokenChar(93); - this.print(node.typeAnnotation, node); - this.tokenChar(59); + this.print(node.typeAnnotation); + this.semicolon(); } function TSAnyKeyword() { this.word("any"); @@ -21029,19 +21033,19 @@ function tsPrintFunctionOrConstructorType(node) { typeParameters } = node; const parameters = node.parameters; - this.print(typeParameters, node); + this.print(typeParameters); this.tokenChar(40); - this._parameters(parameters, node); + this._parameters(parameters); this.tokenChar(41); this.space(); this.token("=>"); this.space(); const returnType = node.typeAnnotation; - this.print(returnType.typeAnnotation, node); + this.print(returnType.typeAnnotation); } function TSTypeReference(node) { - this.print(node.typeName, node, true); - this.print(node.typeParameters, node, true); + this.print(node.typeName, true); + this.print(node.typeParameters, true); } function TSTypePredicate(node) { if (node.asserts) { @@ -21061,7 +21065,7 @@ function TSTypeQuery(node) { this.space(); this.print(node.exprName); if (node.typeParameters) { - this.print(node.typeParameters, node); + this.print(node.typeParameters); } } function TSTypeLiteral(node) { @@ -21076,7 +21080,7 @@ function tsPrintBraced(printer, members, node) { printer.indent(); printer.newline(); for (const member of members) { - printer.print(member, node); + printer.print(member); printer.newline(); } printer.dedent(); @@ -21084,28 +21088,29 @@ function tsPrintBraced(printer, members, node) { printer.rightBrace(node); } function TSArrayType(node) { - this.print(node.elementType, node, true); - this.token("[]"); + this.print(node.elementType, true); + this.tokenChar(91); + this.tokenChar(93); } function TSTupleType(node) { this.tokenChar(91); - this.printList(node.elementTypes, node); + this.printList(node.elementTypes); this.tokenChar(93); } function TSOptionalType(node) { - this.print(node.typeAnnotation, node); + this.print(node.typeAnnotation); this.tokenChar(63); } function TSRestType(node) { this.token("..."); - this.print(node.typeAnnotation, node); + this.print(node.typeAnnotation); } function TSNamedTupleMember(node) { - this.print(node.label, node); + this.print(node.label); if (node.optional) this.tokenChar(63); this.tokenChar(58); this.space(); - this.print(node.elementType, node); + this.print(node.elementType); } function TSUnionType(node) { tsPrintUnionOrIntersectionType(this, node, "|"); @@ -21114,7 +21119,7 @@ function TSIntersectionType(node) { tsPrintUnionOrIntersectionType(this, node, "&"); } function tsPrintUnionOrIntersectionType(printer, node, sep) { - printer.printJoin(node.types, node, { + printer.printJoin(node.types, { separator() { this.space(); this.token(sep); @@ -21144,18 +21149,18 @@ function TSInferType(node) { } function TSParenthesizedType(node) { this.tokenChar(40); - this.print(node.typeAnnotation, node); + this.print(node.typeAnnotation); this.tokenChar(41); } function TSTypeOperator(node) { this.word(node.operator); this.space(); - this.print(node.typeAnnotation, node); + this.print(node.typeAnnotation); } function TSIndexedAccessType(node) { - this.print(node.objectType, node, true); + this.print(node.objectType, true); this.tokenChar(91); - this.print(node.indexType, node); + this.print(node.indexType); this.tokenChar(93); } function TSMappedType(node) { @@ -21163,7 +21168,6 @@ function TSMappedType(node) { nameType, optional, readonly, - typeParameter, typeAnnotation } = node; this.tokenChar(123); @@ -21174,16 +21178,20 @@ function TSMappedType(node) { this.space(); } this.tokenChar(91); - this.word(typeParameter.name); + { + this.word(node.typeParameter.name); + } this.space(); this.word("in"); this.space(); - this.print(typeParameter.constraint, typeParameter); + { + this.print(node.typeParameter.constraint); + } if (nameType) { this.space(); this.word("as"); this.space(); - this.print(nameType, node); + this.print(nameType); } this.tokenChar(93); if (optional) { @@ -21193,7 +21201,7 @@ function TSMappedType(node) { if (typeAnnotation) { this.tokenChar(58); this.space(); - this.print(typeAnnotation, node); + this.print(typeAnnotation); } this.space(); this.tokenChar(125); @@ -21204,11 +21212,11 @@ function tokenIfPlusMinus(self, tok) { } } function TSLiteralType(node) { - this.print(node.literal, node); + this.print(node.literal); } function TSExpressionWithTypeArguments(node) { - this.print(node.expression, node); - this.print(node.typeParameters, node); + this.print(node.expression); + this.print(node.typeParameters); } function TSInterfaceDeclaration(node) { const { @@ -21224,16 +21232,16 @@ function TSInterfaceDeclaration(node) { } this.word("interface"); this.space(); - this.print(id, node); - this.print(typeParameters, node); + this.print(id); + this.print(typeParameters); if (extendz != null && extendz.length) { this.space(); this.word("extends"); this.space(); - this.printList(extendz, node); + this.printList(extendz); } this.space(); - this.print(body, node); + this.print(body); } function TSInterfaceBody(node) { this.tsPrintTypeLiteralOrInterfaceBody(node.body, node); @@ -21251,13 +21259,13 @@ function TSTypeAliasDeclaration(node) { } this.word("type"); this.space(); - this.print(id, node); - this.print(typeParameters, node); + this.print(id); + this.print(typeParameters); this.space(); this.tokenChar(61); this.space(); - this.print(typeAnnotation, node); - this.tokenChar(59); + this.print(typeAnnotation); + this.semicolon(); } function TSTypeExpression(node) { var _expression$trailingC; @@ -21266,12 +21274,12 @@ function TSTypeExpression(node) { expression, typeAnnotation } = node; - const forceParens = !!((_expression$trailingC = expression.trailingComments) != null && _expression$trailingC.length); - this.print(expression, node, true, undefined, forceParens); + const forceParens = (_expression$trailingC = expression.trailingComments) == null ? void 0 : _expression$trailingC.some(c => c.type === "CommentLine" || /[\r\n\u2028\u2029]/.test(c.value)); + this.print(expression, true, undefined, forceParens); this.space(); this.word(type === "TSAsExpression" ? "as" : "satisfies"); this.space(); - this.print(typeAnnotation, node); + this.print(typeAnnotation); } function TSTypeAssertion(node) { const { @@ -21279,14 +21287,14 @@ function TSTypeAssertion(node) { expression } = node; this.tokenChar(60); - this.print(typeAnnotation, node); + this.print(typeAnnotation); this.tokenChar(62); this.space(); - this.print(expression, node); + this.print(expression); } function TSInstantiationExpression(node) { - this.print(node.expression, node); - this.print(node.typeParameters, node); + this.print(node.expression); + this.print(node.typeParameters); } function TSEnumDeclaration(node) { const { @@ -21305,7 +21313,7 @@ function TSEnumDeclaration(node) { } this.word("enum"); this.space(); - this.print(id, node); + this.print(id); this.space(); tsPrintBraced(this, members, node); } @@ -21314,12 +21322,12 @@ function TSEnumMember(node) { id, initializer } = node; - this.print(id, node); + this.print(id); if (initializer) { this.space(); this.tokenChar(61); this.space(); - this.print(initializer, node); + this.print(initializer); } this.tokenChar(44); } @@ -21336,19 +21344,19 @@ function TSModuleDeclaration(node) { this.word(id.type === "Identifier" ? "namespace" : "module"); this.space(); } - this.print(id, node); + this.print(id); if (!node.body) { - this.tokenChar(59); + this.semicolon(); return; } let body = node.body; while (body.type === "TSModuleDeclaration") { this.tokenChar(46); - this.print(body.id, body); + this.print(body.id); body = body.body; } this.space(); - this.print(body, node); + this.print(body); } function TSModuleBlock(node) { tsPrintBraced(this, node.body, node); @@ -21361,14 +21369,14 @@ function TSImportType(node) { } = node; this.word("import"); this.tokenChar(40); - this.print(argument, node); + this.print(argument); this.tokenChar(41); if (qualifier) { this.tokenChar(46); - this.print(qualifier, node); + this.print(qualifier); } if (typeParameters) { - this.print(typeParameters, node); + this.print(typeParameters); } } function TSImportEqualsDeclaration(node) { @@ -21383,20 +21391,20 @@ function TSImportEqualsDeclaration(node) { } this.word("import"); this.space(); - this.print(id, node); + this.print(id); this.space(); this.tokenChar(61); this.space(); - this.print(moduleReference, node); - this.tokenChar(59); + this.print(moduleReference); + this.semicolon(); } function TSExternalModuleReference(node) { this.token("require("); - this.print(node.expression, node); + this.print(node.expression); this.tokenChar(41); } function TSNonNullExpression(node) { - this.print(node.expression, node); + this.print(node.expression); this.tokenChar(33); } function TSExportAssignment(node) { @@ -21404,8 +21412,8 @@ function TSExportAssignment(node) { this.space(); this.tokenChar(61); this.space(); - this.print(node.expression, node); - this.tokenChar(59); + this.print(node.expression); + this.semicolon(); } function TSNamespaceExportDeclaration(node) { this.word("export"); @@ -21414,19 +21422,20 @@ function TSNamespaceExportDeclaration(node) { this.space(); this.word("namespace"); this.space(); - this.print(node.id, node); + this.print(node.id); + this.semicolon(); } function tsPrintSignatureDeclarationBase(node) { const { typeParameters } = node; const parameters = node.parameters; - this.print(typeParameters, node); + this.print(typeParameters); this.tokenChar(40); - this._parameters(parameters, node); + this._parameters(parameters); this.tokenChar(41); const returnType = node.typeAnnotation; - this.print(returnType, node); + this.print(returnType); } function tsPrintClassMemberModifiers(node) { const isField = node.type === "ClassAccessorProperty" || node.type === "ClassProperty"; @@ -21567,6 +21576,7 @@ function generate(ast, opts = {}, code) { Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.TokenContext = void 0; exports.needsParens = needsParens; exports.needsWhitespace = needsWhitespace; exports.needsWhitespaceAfter = needsWhitespaceAfter; @@ -21577,17 +21587,28 @@ var _t = __nccwpck_require__(7912); const { FLIPPED_ALIAS_KEYS, isCallExpression, + isDecorator, isExpressionStatement, isMemberExpression, - isNewExpression + isNewExpression, + isParenthesizedExpression } = _t; +const TokenContext = exports.TokenContext = { + expressionStatement: 1, + arrowBody: 2, + exportDefault: 4, + forHead: 8, + forInHead: 16, + forOfHead: 32, + arrowFlowReturnType: 64 +}; function expandAliases(obj) { const map = new Map(); function add(type, func) { const fn = map.get(type); - map.set(type, fn ? function (node, parent, stack) { + map.set(type, fn ? function (node, parent, stack, inForInit) { var _fn; - return (_fn = fn(node, parent, stack)) != null ? _fn : func(node, parent, stack); + return (_fn = fn(node, parent, stack, inForInit)) != null ? _fn : func(node, parent, stack, inForInit); } : func); } for (const type of Object.keys(obj)) { @@ -21628,13 +21649,26 @@ function needsWhitespaceBefore(node, parent) { function needsWhitespaceAfter(node, parent) { return needsWhitespace(node, parent, 2); } -function needsParens(node, parent, printStack) { +function needsParens(node, parent, tokenContext, inForInit) { var _expandedParens$get; if (!parent) return false; if (isNewExpression(parent) && parent.callee === node) { if (isOrHasCallExpression(node)) return true; } - return (_expandedParens$get = expandedParens.get(node.type)) == null ? void 0 : _expandedParens$get(node, parent, printStack); + if (isDecorator(parent)) { + return !isDecoratorMemberExpression(node) && !(isCallExpression(node) && isDecoratorMemberExpression(node.callee)) && !isParenthesizedExpression(node); + } + return (_expandedParens$get = expandedParens.get(node.type)) == null ? void 0 : _expandedParens$get(node, parent, tokenContext, inForInit); +} +function isDecoratorMemberExpression(node) { + switch (node.type) { + case "Identifier": + return true; + case "MemberExpression": + return !node.computed && node.property.type === "Identifier" && isDecoratorMemberExpression(node.object); + default: + return false; + } } //# sourceMappingURL=index.js.map @@ -21651,12 +21685,11 @@ function needsParens(node, parent, printStack) { Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.ArrowFunctionExpression = ArrowFunctionExpression; exports.AssignmentExpression = AssignmentExpression; exports.Binary = Binary; exports.BinaryExpression = BinaryExpression; exports.ClassExpression = ClassExpression; -exports.ConditionalExpression = ConditionalExpression; +exports.ArrowFunctionExpression = exports.ConditionalExpression = ConditionalExpression; exports.DoExpression = DoExpression; exports.FunctionExpression = FunctionExpression; exports.FunctionTypeAnnotation = FunctionTypeAnnotation; @@ -21667,29 +21700,37 @@ exports.ObjectExpression = ObjectExpression; exports.OptionalIndexedAccessType = OptionalIndexedAccessType; exports.OptionalCallExpression = exports.OptionalMemberExpression = OptionalMemberExpression; exports.SequenceExpression = SequenceExpression; -exports.TSTypeAssertion = exports.TSSatisfiesExpression = exports.TSAsExpression = TSAsExpression; +exports.TSSatisfiesExpression = exports.TSAsExpression = TSAsExpression; exports.TSInferType = TSInferType; exports.TSInstantiationExpression = TSInstantiationExpression; +exports.UnaryLike = exports.TSTypeAssertion = UnaryLike; exports.TSIntersectionType = exports.TSUnionType = TSUnionType; -exports.UnaryLike = UnaryLike; exports.IntersectionTypeAnnotation = exports.UnionTypeAnnotation = UnionTypeAnnotation; exports.UpdateExpression = UpdateExpression; exports.AwaitExpression = exports.YieldExpression = YieldExpression; var _t = __nccwpck_require__(7912); +var _index = __nccwpck_require__(9223); const { isArrayTypeAnnotation, - isArrowFunctionExpression, isBinaryExpression, isCallExpression, - isExportDeclaration, isForOfStatement, isIndexedAccessType, isMemberExpression, isObjectPattern, isOptionalMemberExpression, - isYieldExpression + isYieldExpression, + isStatement } = _t; const PRECEDENCE = new Map([["||", 0], ["??", 0], ["|>", 0], ["&&", 1], ["|", 2], ["^", 3], ["&", 4], ["==", 5], ["===", 5], ["!=", 5], ["!==", 5], ["<", 6], [">", 6], ["<=", 6], [">=", 6], ["in", 6], ["instanceof", 6], [">>", 7], ["<<", 7], [">>>", 7], ["+", 8], ["-", 8], ["*", 9], ["/", 9], ["%", 9], ["**", 10]]); +function getBinaryPrecedence(node, nodeType) { + if (nodeType === "BinaryExpression" || nodeType === "LogicalExpression") { + return PRECEDENCE.get(node.operator); + } + if (nodeType === "TSAsExpression" || nodeType === "TSSatisfiesExpression") { + return PRECEDENCE.get("in"); + } +} function isTSTypeExpression(nodeType) { return nodeType === "TSAsExpression" || nodeType === "TSSatisfiesExpression" || nodeType === "TSTypeAssertion"; } @@ -21704,23 +21745,25 @@ const hasPostfixPart = (node, parent) => { function NullableTypeAnnotation(node, parent) { return isArrayTypeAnnotation(parent); } -function FunctionTypeAnnotation(node, parent, printStack) { - if (printStack.length < 3) return; +function FunctionTypeAnnotation(node, parent, tokenContext) { const parentType = parent.type; - return parentType === "UnionTypeAnnotation" || parentType === "IntersectionTypeAnnotation" || parentType === "ArrayTypeAnnotation" || parentType === "TypeAnnotation" && isArrowFunctionExpression(printStack[printStack.length - 3]); + return parentType === "UnionTypeAnnotation" || parentType === "IntersectionTypeAnnotation" || parentType === "ArrayTypeAnnotation" || Boolean(tokenContext & _index.TokenContext.arrowFlowReturnType); } function UpdateExpression(node, parent) { return hasPostfixPart(node, parent) || isClassExtendsClause(node, parent); } -function ObjectExpression(node, parent, printStack) { - return isFirstInContext(printStack, 1 | 2); +function needsParenBeforeExpressionBrace(tokenContext) { + return Boolean(tokenContext & (_index.TokenContext.expressionStatement | _index.TokenContext.arrowBody)); +} +function ObjectExpression(node, parent, tokenContext) { + return needsParenBeforeExpressionBrace(tokenContext); } -function DoExpression(node, parent, printStack) { - return !node.async && isFirstInContext(printStack, 1); +function DoExpression(node, parent, tokenContext) { + return !node.async && Boolean(tokenContext & _index.TokenContext.expressionStatement); } function Binary(node, parent) { const parentType = parent.type; - if (node.operator === "**" && parentType === "BinaryExpression" && parent.operator === "**") { + if (node.type === "BinaryExpression" && node.operator === "**" && parentType === "BinaryExpression" && parent.operator === "**") { return parent.left === node; } if (isClassExtendsClause(node, parent)) { @@ -21729,10 +21772,10 @@ function Binary(node, parent) { if (hasPostfixPart(node, parent) || parentType === "UnaryExpression" || parentType === "SpreadElement" || parentType === "AwaitExpression") { return true; } - if (parentType === "BinaryExpression" || parentType === "LogicalExpression") { - const parentPos = PRECEDENCE.get(parent.operator); - const nodePos = PRECEDENCE.get(node.operator); - if (parentPos === nodePos && parent.right === node && parentType !== "LogicalExpression" || parentPos > nodePos) { + const parentPos = getBinaryPrecedence(parent, parentType); + if (parentPos != null) { + const nodePos = getBinaryPrecedence(node, node.type); + if (parentPos === nodePos && parentType === "BinaryExpression" && parent.right === node || parentPos > nodePos) { return true; } } @@ -21745,12 +21788,18 @@ function UnionTypeAnnotation(node, parent) { function OptionalIndexedAccessType(node, parent) { return isIndexedAccessType(parent) && parent.objectType === node; } -function TSAsExpression() { - return true; +function TSAsExpression(node, parent) { + if ((parent.type === "AssignmentExpression" || parent.type === "AssignmentPattern") && parent.left === node) { + return true; + } + if (parent.type === "BinaryExpression" && (parent.operator === "|" || parent.operator === "&") && node === parent.left) { + return true; + } + return Binary(node, parent); } function TSUnionType(node, parent) { const parentType = parent.type; - return parentType === "TSArrayType" || parentType === "TSOptionalType" || parentType === "TSIntersectionType" || parentType === "TSUnionType" || parentType === "TSRestType"; + return parentType === "TSArrayType" || parentType === "TSOptionalType" || parentType === "TSIntersectionType" || parentType === "TSRestType"; } function TSInferType(node, parent) { const parentType = parent.type; @@ -21760,35 +21809,37 @@ function TSInstantiationExpression(node, parent) { const parentType = parent.type; return (parentType === "CallExpression" || parentType === "OptionalCallExpression" || parentType === "NewExpression" || parentType === "TSInstantiationExpression") && !!parent.typeParameters; } -function BinaryExpression(node, parent) { - if (node.operator === "in") { - const parentType = parent.type; - return parentType === "VariableDeclarator" || parentType === "ForStatement" || parentType === "ForInStatement" || parentType === "ForOfStatement"; - } - return false; +function BinaryExpression(node, parent, tokenContext, inForStatementInit) { + return node.operator === "in" && inForStatementInit; } function SequenceExpression(node, parent) { const parentType = parent.type; - if (parentType === "ForStatement" || parentType === "ThrowStatement" || parentType === "ReturnStatement" || parentType === "IfStatement" && parent.test === node || parentType === "WhileStatement" && parent.test === node || parentType === "ForInStatement" && parent.right === node || parentType === "SwitchStatement" && parent.discriminant === node || parentType === "ExpressionStatement" && parent.expression === node) { + if (parentType === "SequenceExpression" || parentType === "ParenthesizedExpression" || parentType === "MemberExpression" && parent.property === node || parentType === "OptionalMemberExpression" && parent.property === node || parentType === "TemplateLiteral") { return false; } - return true; + if (parentType === "ClassDeclaration") { + return true; + } + if (parentType === "ForOfStatement") { + return parent.right === node; + } + if (parentType === "ExportDefaultDeclaration") { + return true; + } + return !isStatement(parent); } function YieldExpression(node, parent) { const parentType = parent.type; - return parentType === "BinaryExpression" || parentType === "LogicalExpression" || parentType === "UnaryExpression" || parentType === "SpreadElement" || hasPostfixPart(node, parent) || parentType === "AwaitExpression" && isYieldExpression(node) || parentType === "ConditionalExpression" && node === parent.test || isClassExtendsClause(node, parent); + return parentType === "BinaryExpression" || parentType === "LogicalExpression" || parentType === "UnaryExpression" || parentType === "SpreadElement" || hasPostfixPart(node, parent) || parentType === "AwaitExpression" && isYieldExpression(node) || parentType === "ConditionalExpression" && node === parent.test || isClassExtendsClause(node, parent) || isTSTypeExpression(parentType); } -function ClassExpression(node, parent, printStack) { - return isFirstInContext(printStack, 1 | 4); +function ClassExpression(node, parent, tokenContext) { + return Boolean(tokenContext & (_index.TokenContext.expressionStatement | _index.TokenContext.exportDefault)); } function UnaryLike(node, parent) { return hasPostfixPart(node, parent) || isBinaryExpression(parent) && parent.operator === "**" && parent.left === node || isClassExtendsClause(node, parent); } -function FunctionExpression(node, parent, printStack) { - return isFirstInContext(printStack, 1 | 4); -} -function ArrowFunctionExpression(node, parent) { - return isExportDeclaration(parent) || ConditionalExpression(node, parent); +function FunctionExpression(node, parent, tokenContext) { + return Boolean(tokenContext & (_index.TokenContext.expressionStatement | _index.TokenContext.exportDefault)); } function ConditionalExpression(node, parent) { const parentType = parent.type; @@ -21800,8 +21851,8 @@ function ConditionalExpression(node, parent) { function OptionalMemberExpression(node, parent) { return isCallExpression(parent) && parent.callee === node || isMemberExpression(parent) && parent.object === node; } -function AssignmentExpression(node, parent) { - if (isObjectPattern(node.left)) { +function AssignmentExpression(node, parent, tokenContext) { + if (needsParenBeforeExpressionBrace(tokenContext) && isObjectPattern(node.left)) { return true; } else { return ConditionalExpression(node, parent); @@ -21820,7 +21871,7 @@ function LogicalExpression(node, parent) { return parent.operator !== "??"; } } -function Identifier(node, parent, printStack) { +function Identifier(node, parent, tokenContext) { var _node$extra; const parentType = parent.type; if ((_node$extra = node.extra) != null && _node$extra.parenthesized && parentType === "AssignmentExpression" && parent.left === node) { @@ -21838,36 +21889,15 @@ function Identifier(node, parent, printStack) { computed: true, optional: false }); - return isFirstInContext(printStack, isFollowedByBracket ? 1 | 8 | 16 | 32 : 32); - } - return node.name === "async" && isForOfStatement(parent) && node === parent.left; -} -function isFirstInContext(printStack, checkParam) { - const expressionStatement = checkParam & 1; - const arrowBody = checkParam & 2; - const exportDefault = checkParam & 4; - const forHead = checkParam & 8; - const forInHead = checkParam & 16; - const forOfHead = checkParam & 32; - let i = printStack.length - 1; - if (i <= 0) return; - let node = printStack[i]; - i--; - let parent = printStack[i]; - while (i >= 0) { - const parentType = parent.type; - if (expressionStatement && parentType === "ExpressionStatement" && parent.expression === node || exportDefault && parentType === "ExportDefaultDeclaration" && node === parent.declaration || arrowBody && parentType === "ArrowFunctionExpression" && parent.body === node || forHead && parentType === "ForStatement" && parent.init === node || forInHead && parentType === "ForInStatement" && parent.left === node || forOfHead && parentType === "ForOfStatement" && parent.left === node) { + if (isFollowedByBracket && tokenContext & (_index.TokenContext.expressionStatement | _index.TokenContext.forHead | _index.TokenContext.forInHead)) { return true; } - if (i > 0 && (hasPostfixPart(node, parent) && parentType !== "NewExpression" || parentType === "SequenceExpression" && parent.expressions[0] === node || parentType === "UpdateExpression" && !parent.prefix || parentType === "ConditionalExpression" && parent.test === node || (parentType === "BinaryExpression" || parentType === "LogicalExpression") && parent.left === node || parentType === "AssignmentExpression" && parent.left === node)) { - node = parent; - i--; - parent = printStack[i]; - } else { - return false; - } + return Boolean(tokenContext & _index.TokenContext.forOfHead); } - return false; + return node.name === "async" && isForOfStatement(parent, { + left: node, + await: false + }); } //# sourceMappingURL=parentheses.js.map @@ -22058,8 +22088,9 @@ const { } = n; class Printer { constructor(format, map) { - this.inForStatementInitCounter = 0; - this._printStack = []; + this.inForStatementInit = false; + this.tokenContext = 0; + this._currentNode = null; this._indent = 0; this._indentRepeat = 0; this._insideAux = false; @@ -22069,6 +22100,7 @@ class Printer { this._printedComments = new Set(); this._endsWithInteger = false; this._endsWithWord = false; + this._endsWithDiv = false; this._lastCommentLine = 0; this._endsWithInnerRaw = false; this._indentInnerComments = true; @@ -22077,6 +22109,14 @@ class Printer { this._inputMap = map == null ? void 0 : map._inputMap; this._buf = new _buffer.default(map, format.indent.style[0]); } + enterForStatementInit(val) { + const old = this.inForStatementInit; + if (old === val) return () => {}; + this.inForStatementInit = val; + return () => { + this.inForStatementInit = old; + }; + } generate(ast) { this.print(ast); this._maybeAddAuxComment(); @@ -22122,8 +22162,9 @@ class Printer { } } word(str, noLineTerminatorAfter = false) { + this.tokenContext = 0; this._maybePrintInnerComments(); - if (this._endsWithWord || str.charCodeAt(0) === 47 && this.endsWith(47)) { + if (this._endsWithWord || this._endsWithDiv && str.charCodeAt(0) === 47) { this._space(); } this._maybeAddAuxComment(); @@ -22143,6 +22184,7 @@ class Printer { this._endsWithInteger = Number.isInteger(number) && !isNonDecimalLiteral(str) && !SCIENTIFIC_NOTATION.test(str) && !ZERO_DECIMAL_INTEGER.test(str) && str.charCodeAt(str.length - 1) !== 46; } token(str, maybeNewline = false) { + this.tokenContext = 0; this._maybePrintInnerComments(); const lastChar = this.getLastChar(); const strFirst = str.charCodeAt(0); @@ -22154,6 +22196,7 @@ class Printer { this._noLineTerminator = false; } tokenChar(char) { + this.tokenContext = 0; this._maybePrintInnerComments(); const lastChar = this.getLastChar(); if (char === 43 && lastChar === 43 || char === 45 && lastChar === 45 || char === 46 && this._endsWithInteger) { @@ -22209,14 +22252,6 @@ class Printer { this._catchUp(prop, loc); this._buf.sourceWithOffset(prop, loc, columnOffset); } - withSource(prop, loc, cb) { - if (!loc) { - cb(); - return; - } - this._catchUp(prop, loc); - this._buf.withSource(prop, loc, cb); - } sourceIdentifierName(identifierName, pos) { if (!this._buf._canMarkIdName) return; const sourcePosition = this._buf._sourcePosition; @@ -22235,6 +22270,7 @@ class Printer { this._buf.append(str, maybeNewline); this._endsWithWord = false; this._endsWithInteger = false; + this._endsWithDiv = false; } _appendChar(char) { this._maybeAddParenChar(char); @@ -22242,6 +22278,7 @@ class Printer { this._buf.appendChar(char); this._endsWithWord = false; this._endsWithInteger = false; + this._endsWithDiv = false; } _queue(char) { this._maybeAddParenChar(char); @@ -22322,16 +22359,16 @@ class Printer { _getIndent() { return this._indentRepeat * this._indent; } - printTerminatorless(node, parent, isLabel) { + printTerminatorless(node, isLabel) { if (isLabel) { this._noLineTerminator = true; - this.print(node, parent); + this.print(node); } else { const terminatorState = { printed: false }; this._parenPushNewlineState = terminatorState; - this.print(node, parent); + this.print(node); if (terminatorState.printed) { this.dedent(); this.newline(); @@ -22339,7 +22376,7 @@ class Printer { } } } - print(node, parent, noLineTerminatorAfter, trailingCommentsLineOffset, forceParens) { + print(node, noLineTerminatorAfter, trailingCommentsLineOffset, forceParens) { var _node$extra, _node$leadingComments; if (!node) return; this._endsWithInnerRaw = false; @@ -22353,12 +22390,13 @@ class Printer { if (printMethod === undefined) { throw new ReferenceError(`unknown node of type ${JSON.stringify(nodeType)} with constructor ${JSON.stringify(node.constructor.name)}`); } - this._printStack.push(node); + const parent = this._currentNode; + this._currentNode = node; const oldInAux = this._insideAux; this._insideAux = node.loc == null; this._maybeAddAuxComment(this._insideAux && !oldInAux); const parenthesized = (_node$extra = node.extra) == null ? void 0 : _node$extra.parenthesized; - let shouldPrintParens = forceParens || parenthesized && format.retainFunctionParens && nodeType === "FunctionExpression" || needsParens(node, parent, this._printStack); + let shouldPrintParens = forceParens || parenthesized && format.retainFunctionParens && nodeType === "FunctionExpression" || needsParens(node, parent, this.tokenContext, this.inForStatementInit); if (!shouldPrintParens && parenthesized && (_node$leadingComments = node.leadingComments) != null && _node$leadingComments.length && node.leadingComments[0].type === "CommentBlock") { const parentType = parent == null ? void 0 : parent.type; switch (parentType) { @@ -22375,9 +22413,11 @@ class Printer { shouldPrintParens = true; } } + let exitInForStatementInit; if (shouldPrintParens) { this.tokenChar(40); this._endsWithInnerRaw = false; + exitInForStatementInit = this.enterForStatementInit(false); } this._lastCommentLine = 0; this._printLeadingComments(node, parent); @@ -22387,13 +22427,14 @@ class Printer { this._printTrailingComments(node, parent); this.tokenChar(41); this._noLineTerminator = noLineTerminatorAfter; + exitInForStatementInit(); } else if (noLineTerminatorAfter && !this._noLineTerminator) { this._noLineTerminator = true; this._printTrailingComments(node, parent); } else { this._printTrailingComments(node, parent, trailingCommentsLineOffset); } - this._printStack.pop(); + this._currentNode = parent; format.concise = oldConcise; this._insideAux = oldInAux; this._endsWithInnerRaw = false; @@ -22430,7 +22471,7 @@ class Printer { return extra.raw; } } - printJoin(nodes, parent, opts = {}) { + printJoin(nodes, opts = {}) { if (!(nodes != null && nodes.length)) return; let { indent @@ -22453,7 +22494,7 @@ class Printer { const node = nodes[i]; if (!node) continue; if (opts.statement) this._printNewline(i === 0, newlineOpts); - this.print(node, parent, undefined, opts.trailingCommentsLineOffset || 0); + this.print(node, undefined, opts.trailingCommentsLineOffset || 0); opts.iterator == null || opts.iterator(node, i); if (i < len - 1) separator == null || separator(); if (opts.statement) { @@ -22473,10 +22514,10 @@ class Printer { } if (indent) this.dedent(); } - printAndIndentOnComments(node, parent) { + printAndIndentOnComments(node) { const indent = node.leadingComments && node.leadingComments.length > 0; if (indent) this.indent(); - this.print(node, parent); + this.print(node); if (indent) this.dedent(); } printBlock(parent) { @@ -22484,7 +22525,7 @@ class Printer { if (node.type !== "EmptyStatement") { this.space(); } - this.print(node, parent); + this.print(node); } _printTrailingComments(node, parent, lineOffset) { const { @@ -22509,7 +22550,7 @@ class Printer { this._indentInnerComments = true; } printInnerComments() { - const node = this._printStack[this._printStack.length - 1]; + const node = this._currentNode; const comments = node.innerComments; if (!(comments != null && comments.length)) return; const hasSpace = this.endsWith(32); @@ -22525,17 +22566,17 @@ class Printer { noIndentInnerCommentsHere() { this._indentInnerComments = false; } - printSequence(nodes, parent, opts = {}) { + printSequence(nodes, opts = {}) { var _opts$indent; opts.statement = true; (_opts$indent = opts.indent) != null ? _opts$indent : opts.indent = false; - this.printJoin(nodes, parent, opts); + this.printJoin(nodes, opts); } - printList(items, parent, opts = {}) { + printList(items, opts = {}) { if (opts.separator == null) { opts.separator = commaSeparator; } - this.printJoin(items, parent, opts); + this.printJoin(items, opts); } _printNewline(newLine, opts) { const format = this.format; @@ -22580,7 +22621,7 @@ class Printer { this.newline(1); } const lastCharCode = this.getLastChar(); - if (lastCharCode !== 91 && lastCharCode !== 123) { + if (lastCharCode !== 91 && lastCharCode !== 123 && lastCharCode !== 40) { this.space(); } let val; @@ -22616,7 +22657,7 @@ class Printer { } else { val = `/*${comment.value}*/`; } - if (this.endsWith(47)) this._space(); + if (this._endsWithDiv) this._space(); this.source("start", comment.loc); this._append(val, isBlockComment); if (!isBlockComment && !noLineTerminator) { @@ -23335,7 +23376,7 @@ function readEscapedChar(input, pos, lineStart, curLine, inTemplate, errors) { default: if (ch >= 48 && ch <= 55) { const startPos = pos - 1; - const match = input.slice(startPos, pos + 2).match(/^[0-7]+/); + const match = /^[0-7]+/.exec(input.slice(startPos, pos + 2)); let octalStr = match[0]; let octal = parseInt(octalStr, 8); if (octal > 255) { @@ -23800,17 +23841,14 @@ function highlight(code, options = {}) { Object.defineProperty(exports, "__esModule", ({ value: true })); -function _objectWithoutPropertiesLoose(source, excluded) { - if (source == null) return {}; - var target = {}; - var sourceKeys = Object.keys(source); - var key, i; - for (i = 0; i < sourceKeys.length; i++) { - key = sourceKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - target[key] = source[key]; +function _objectWithoutPropertiesLoose(r, e) { + if (null == r) return {}; + var t = {}; + for (var n in r) if ({}.hasOwnProperty.call(r, n)) { + if (e.includes(n)) continue; + t[n] = r[n]; } - return target; + return t; } class Position { constructor(line, col, index) { @@ -24130,8 +24168,7 @@ var PipelineOperatorErrors = { PrimaryTopicNotAllowed: "Topic reference was used in a lexical context without topic binding.", PrimaryTopicRequiresSmartPipeline: 'Topic reference is used, but the pipelineOperator plugin was not passed a "proposal": "hack" or "smart" option.' }; -const _excluded = ["toMessage"], - _excluded2 = ["message"]; +const _excluded = ["message"]; function defineHidden(obj, key, value) { Object.defineProperty(obj, key, { enumerable: false, @@ -24139,21 +24176,35 @@ function defineHidden(obj, key, value) { value }); } -function toParseErrorConstructor(_ref) { - let { - toMessage - } = _ref, - properties = _objectWithoutPropertiesLoose(_ref, _excluded); +function toParseErrorConstructor({ + toMessage, + code, + reasonCode, + syntaxPlugin +}) { + const hasMissingPlugin = reasonCode === "MissingPlugin" || reasonCode === "MissingOneOfPlugins"; + { + const oldReasonCodes = { + AccessorCannotDeclareThisParameter: "AccesorCannotDeclareThisParameter", + AccessorCannotHaveTypeParameters: "AccesorCannotHaveTypeParameters", + ConstInitializerMustBeStringOrNumericLiteralOrLiteralEnumReference: "ConstInitiailizerMustBeStringOrNumericLiteralOrLiteralEnumReference", + SetAccessorCannotHaveOptionalParameter: "SetAccesorCannotHaveOptionalParameter", + SetAccessorCannotHaveRestParameter: "SetAccesorCannotHaveRestParameter", + SetAccessorCannotHaveReturnType: "SetAccesorCannotHaveReturnType" + }; + if (oldReasonCodes[reasonCode]) { + reasonCode = oldReasonCodes[reasonCode]; + } + } return function constructor(loc, details) { const error = new SyntaxError(); - Object.assign(error, properties, { - loc, - pos: loc.index - }); - if ("missingPlugin" in details) { - Object.assign(error, { - missingPlugin: details.missingPlugin - }); + error.code = code; + error.reasonCode = reasonCode; + error.loc = loc; + error.pos = loc.index; + error.syntaxPlugin = syntaxPlugin; + if (hasMissingPlugin) { + error.missingPlugin = details.missingPlugin; } defineHidden(error, "clone", function clone(overrides = {}) { var _overrides$loc; @@ -24189,15 +24240,15 @@ function ParseErrorEnum(argument, syntaxPlugin) { const ParseErrorConstructors = {}; for (const reasonCode of Object.keys(argument)) { const template = argument[reasonCode]; - const _ref2 = typeof template === "string" ? { + const _ref = typeof template === "string" ? { message: () => template } : typeof template === "function" ? { message: template } : template, { message - } = _ref2, - rest = _objectWithoutPropertiesLoose(_ref2, _excluded2); + } = _ref, + rest = _objectWithoutPropertiesLoose(_ref, _excluded); const toMessage = typeof message === "string" ? () => message : message; ParseErrorConstructors[reasonCode] = toParseErrorConstructor(Object.assign({ code: "BABEL_PARSER_SYNTAX_ERROR", @@ -24213,13 +24264,17 @@ const Errors = Object.assign({}, ParseErrorEnum(ModuleErrors), ParseErrorEnum(St const { defineProperty } = Object; -const toUnenumerable = (object, key) => defineProperty(object, key, { - enumerable: false, - value: object[key] -}); +const toUnenumerable = (object, key) => { + if (object) { + defineProperty(object, key, { + enumerable: false, + value: object[key] + }); + } +}; function toESTreeLocation(node) { - node.loc.start && toUnenumerable(node.loc.start, "index"); - node.loc.end && toUnenumerable(node.loc.end, "index"); + toUnenumerable(node.loc.start, "index"); + toUnenumerable(node.loc.end, "index"); return node; } var estree = superClass => class ESTreeParserMixin extends superClass { @@ -24237,7 +24292,7 @@ var estree = superClass => class ESTreeParserMixin extends superClass { let regex = null; try { regex = new RegExp(pattern, flags); - } catch (e) {} + } catch (_) {} const node = this.estreeParseLiteral(regex); node.regex = { pattern, @@ -25583,7 +25638,7 @@ class CommentsParser extends BaseParser { } } } -const lineBreak = /\r\n?|[\n\u2028\u2029]/; +const lineBreak = /\r\n|[\r\n\u2028\u2029]/; const lineBreakG = new RegExp(lineBreak.source, "g"); function isNewLine(code) { switch (code) { @@ -25596,9 +25651,16 @@ function isNewLine(code) { return false; } } +function hasNewLine(input, start, end) { + for (let i = start; i < end; i++) { + if (isNewLine(input.charCodeAt(i))) { + return true; + } + } + return false; +} const skipWhiteSpace = /(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g; const skipWhiteSpaceInLine = /(?:[^\S\n\r\u2028\u2029]|\/\/.*|\/\*.*?\*\/)*/g; -const skipWhiteSpaceToLineBreak = new RegExp("(?=(" + skipWhiteSpaceInLine.source + "))\\1" + /(?=[\n\r\u2028\u2029]|\/\*(?!.*?\*\/)|$)/.source, "y"); function isWhitespace(code) { switch (code) { case 0x0009: @@ -25740,6 +25802,12 @@ class State { set containsEsc(v) { if (v) this.flags |= 2048;else this.flags &= -2049; } + get hasTopLevelAwait() { + return (this.flags & 4096) > 0; + } + set hasTopLevelAwait(v) { + if (v) this.flags |= 4096;else this.flags &= -4097; + } curPosition() { return new Position(this.curLine, this.pos - this.lineStart, this.pos); } @@ -25920,7 +25988,7 @@ function readEscapedChar(input, pos, lineStart, curLine, inTemplate, errors) { default: if (ch >= 48 && ch <= 55) { const startPos = pos - 1; - const match = input.slice(startPos, pos + 2).match(/^[0-7]+/); + const match = /^[0-7]+/.exec(input.slice(startPos, pos + 2)); let octalStr = match[0]; let octal = parseInt(octalStr, 8); if (octal > 255) { @@ -27293,7 +27361,13 @@ function functionFlags(isAsync, isGenerator) { class UtilParser extends Tokenizer { addExtra(node, key, value, enumerable = true) { if (!node) return; - const extra = node.extra = node.extra || {}; + let { + extra + } = node; + if (extra == null) { + extra = {}; + node.extra = extra; + } if (enumerable) { extra[key] = value; } else { @@ -27337,11 +27411,10 @@ class UtilParser extends Tokenizer { return this.match(139) || this.match(8) || this.hasPrecedingLineBreak(); } hasPrecedingLineBreak() { - return lineBreak.test(this.input.slice(this.state.lastTokEndLoc.index, this.state.start)); + return hasNewLine(this.input, this.state.lastTokEndLoc.index, this.state.start); } hasFollowingLineBreak() { - skipWhiteSpaceToLineBreak.lastIndex = this.state.end; - return skipWhiteSpaceToLineBreak.test(this.input); + return hasNewLine(this.input, this.state.end, this.nextTokenStart()); } isLineTerminator() { return this.eat(13) || this.canInsertSemicolon(); @@ -27351,7 +27424,9 @@ class UtilParser extends Tokenizer { this.raise(Errors.MissingSemicolon, this.state.lastTokEndLoc); } expect(type, loc) { - this.eat(type) || this.unexpected(loc, type); + if (!this.eat(type)) { + this.unexpected(loc, type); + } } tryParse(fn, oldState = this.state.clone()) { const abortSignal = { @@ -28956,7 +29031,7 @@ var flow = superClass => class FlowParserMixin extends superClass { } forwardNoArrowParamsConversionAt(node, parse) { let result; - if (this.state.noArrowParamsConversionAt.indexOf(node.start) !== -1) { + if (this.state.noArrowParamsConversionAt.includes(node.start)) { this.state.noArrowParamsConversionAt.push(this.state.start); result = parse(); this.state.noArrowParamsConversionAt.pop(); @@ -29466,14 +29541,14 @@ var flow = superClass => class FlowParserMixin extends superClass { return this.match(14) || super.shouldParseArrow(params); } setArrowFunctionParameters(node, params) { - if (this.state.noArrowParamsConversionAt.indexOf(node.start) !== -1) { + if (this.state.noArrowParamsConversionAt.includes(node.start)) { node.params = params; } else { super.setArrowFunctionParameters(node, params); } } checkParams(node, allowDuplicates, isArrowFunction, strictModeChanged = true) { - if (isArrowFunction && this.state.noArrowParamsConversionAt.indexOf(node.start) !== -1) { + if (isArrowFunction && this.state.noArrowParamsConversionAt.includes(node.start)) { return; } for (let i = 0; i < node.params.length; i++) { @@ -29484,10 +29559,10 @@ var flow = superClass => class FlowParserMixin extends superClass { super.checkParams(node, allowDuplicates, isArrowFunction, strictModeChanged); } parseParenAndDistinguishExpression(canBeArrow) { - return super.parseParenAndDistinguishExpression(canBeArrow && this.state.noArrowAt.indexOf(this.state.start) === -1); + return super.parseParenAndDistinguishExpression(canBeArrow && !this.state.noArrowAt.includes(this.state.start)); } parseSubscripts(base, startLoc, noCalls) { - if (base.type === "Identifier" && base.name === "async" && this.state.noArrowAt.indexOf(startLoc.index) !== -1) { + if (base.type === "Identifier" && base.name === "async" && this.state.noArrowAt.includes(startLoc.index)) { this.next(); const node = this.startNodeAt(startLoc); node.callee = base; @@ -30748,7 +30823,6 @@ class TypeScriptScopeHandler extends ScopeHandler { super.checkLocalExport(id); } } -const getOwn$1 = (object, key) => hasOwnProperty.call(object, key) && object[key]; const unwrapParenthesizedExpression = node => { return node.type === "ParenthesizedExpression" ? unwrapParenthesizedExpression(node.expression) : node; }; @@ -31007,25 +31081,26 @@ class LValParser extends NodeUtils { return this.finishNode(node, "AssignmentPattern"); } isValidLVal(type, isUnparenthesizedInAssign, binding) { - return getOwn$1({ - AssignmentPattern: "left", - RestElement: "argument", - ObjectProperty: "value", - ParenthesizedExpression: "expression", - ArrayPattern: "elements", - ObjectPattern: "properties" - }, type); + switch (type) { + case "AssignmentPattern": + return "left"; + case "RestElement": + return "argument"; + case "ObjectProperty": + return "value"; + case "ParenthesizedExpression": + return "expression"; + case "ArrayPattern": + return "elements"; + case "ObjectPattern": + return "properties"; + } + return false; } isOptionalMemberExpression(expression) { return expression.type === "OptionalMemberExpression"; } - checkLVal(expression, { - in: ancestor, - binding = 64, - checkClashes = false, - strictModeChanged = false, - hasParenthesizedAncestor = false - }) { + checkLVal(expression, ancestor, binding = 64, checkClashes = false, strictModeChanged = false, hasParenthesizedAncestor = false) { var _expression$extra; const type = expression.type; if (this.isObjectMethod(expression)) return; @@ -31067,20 +31142,25 @@ class LValParser extends NodeUtils { }); return; } - const [key, isParenthesizedExpression] = Array.isArray(validity) ? validity : [validity, type === "ParenthesizedExpression"]; + let key, isParenthesizedExpression; + if (typeof validity === "string") { + key = validity; + isParenthesizedExpression = type === "ParenthesizedExpression"; + } else { + [key, isParenthesizedExpression] = validity; + } const nextAncestor = type === "ArrayPattern" || type === "ObjectPattern" ? { type } : ancestor; - for (const child of [].concat(expression[key])) { - if (child) { - this.checkLVal(child, { - in: nextAncestor, - binding, - checkClashes, - strictModeChanged, - hasParenthesizedAncestor: isParenthesizedExpression - }); + const val = expression[key]; + if (Array.isArray(val)) { + for (const child of val) { + if (child) { + this.checkLVal(child, nextAncestor, binding, checkClashes, strictModeChanged, isParenthesizedExpression); + } } + } else if (val) { + this.checkLVal(val, nextAncestor, binding, checkClashes, strictModeChanged, isParenthesizedExpression); } } checkIdentifier(at, bindingType, strictModeChanged = false) { @@ -31128,7 +31208,6 @@ class LValParser extends NodeUtils { return true; } } -const getOwn = (object, key) => hasOwnProperty.call(object, key) && object[key]; function nonNull(x) { if (x == null) { throw new Error(`Unexpected ${x} value.`); @@ -31147,12 +31226,12 @@ const TSErrors = ParseErrorEnum`typescript`({ AbstractPropertyHasInitializer: ({ propertyName }) => `Property '${propertyName}' cannot have an initializer because it is marked abstract.`, - AccesorCannotDeclareThisParameter: "'get' and 'set' accessors cannot declare 'this' parameters.", - AccesorCannotHaveTypeParameters: "An accessor cannot have type parameters.", AccessorCannotBeOptional: "An 'accessor' property cannot be declared optional.", + AccessorCannotDeclareThisParameter: "'get' and 'set' accessors cannot declare 'this' parameters.", + AccessorCannotHaveTypeParameters: "An accessor cannot have type parameters.", ClassMethodHasDeclare: "Class methods cannot have the 'declare' modifier.", ClassMethodHasReadonly: "Class methods cannot have the 'readonly' modifier.", - ConstInitiailizerMustBeStringOrNumericLiteralOrLiteralEnumReference: "A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.", + ConstInitializerMustBeStringOrNumericLiteralOrLiteralEnumReference: "A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.", ConstructorHasTypeParameters: "Type parameters cannot appear on a constructor declaration.", DeclareAccessor: ({ kind @@ -31211,9 +31290,9 @@ const TSErrors = ParseErrorEnum`typescript`({ ReadonlyForMethodSignature: "'readonly' modifier can only appear on a property declaration or index signature.", ReservedArrowTypeParam: "This syntax is reserved in files with the .mts or .cts extension. Add a trailing comma, as in `() => ...`.", ReservedTypeAssertion: "This syntax is reserved in files with the .mts or .cts extension. Use an `as` expression instead.", - SetAccesorCannotHaveOptionalParameter: "A 'set' accessor cannot have an optional parameter.", - SetAccesorCannotHaveRestParameter: "A 'set' accessor cannot have rest parameter.", - SetAccesorCannotHaveReturnType: "A 'set' accessor cannot have a return type annotation.", + SetAccessorCannotHaveOptionalParameter: "A 'set' accessor cannot have an optional parameter.", + SetAccessorCannotHaveRestParameter: "A 'set' accessor cannot have rest parameter.", + SetAccessorCannotHaveReturnType: "A 'set' accessor cannot have a return type annotation.", SingleTypeParameterWithoutTrailingComma: ({ typeParameterName }) => `Single type parameter ${typeParameterName} should have a trailing comma. Example usage: <${typeParameterName},>.`, @@ -31291,18 +31370,28 @@ var typescript = superClass => class TypeScriptParserMixin extends superClass { return tokenIsIdentifier(this.state.type); } tsTokenCanFollowModifier() { - return (this.match(0) || this.match(5) || this.match(55) || this.match(21) || this.match(138) || this.isLiteralPropertyName()) && !this.hasPrecedingLineBreak(); + return this.match(0) || this.match(5) || this.match(55) || this.match(21) || this.match(138) || this.isLiteralPropertyName(); } - tsNextTokenCanFollowModifier() { + tsNextTokenOnSameLineAndCanFollowModifier() { this.next(); + if (this.hasPrecedingLineBreak()) { + return false; + } return this.tsTokenCanFollowModifier(); } + tsNextTokenCanFollowModifier() { + if (this.match(106)) { + this.next(); + return this.tsTokenCanFollowModifier(); + } + return this.tsNextTokenOnSameLineAndCanFollowModifier(); + } tsParseModifier(allowedModifiers, stopOnStartOfClassStaticBlock) { if (!tokenIsIdentifier(this.state.type) && this.state.type !== 58 && this.state.type !== 75) { return undefined; } const modifier = this.state.value; - if (allowedModifiers.indexOf(modifier) !== -1) { + if (allowedModifiers.includes(modifier)) { if (stopOnStartOfClassStaticBlock && this.tsIsStartOfStaticBlocks()) { return undefined; } @@ -31620,7 +31709,7 @@ var typescript = superClass => class TypeScriptParserMixin extends superClass { } const method = nodeAny; if (method.kind && this.match(47)) { - this.raise(TSErrors.AccesorCannotHaveTypeParameters, this.state.curPosition()); + this.raise(TSErrors.AccessorCannotHaveTypeParameters, this.state.curPosition()); } this.tsFillSignature(14, method); this.tsParseTypeMemberSemicolon(); @@ -31630,7 +31719,7 @@ var typescript = superClass => class TypeScriptParserMixin extends superClass { if (method[paramsKey].length > 0) { this.raise(Errors.BadGetterArity, this.state.curPosition()); if (this.isThisParam(method[paramsKey][0])) { - this.raise(TSErrors.AccesorCannotDeclareThisParameter, this.state.curPosition()); + this.raise(TSErrors.AccessorCannotDeclareThisParameter, this.state.curPosition()); } } } else if (method.kind === "set") { @@ -31639,17 +31728,17 @@ var typescript = superClass => class TypeScriptParserMixin extends superClass { } else { const firstParameter = method[paramsKey][0]; if (this.isThisParam(firstParameter)) { - this.raise(TSErrors.AccesorCannotDeclareThisParameter, this.state.curPosition()); + this.raise(TSErrors.AccessorCannotDeclareThisParameter, this.state.curPosition()); } if (firstParameter.type === "Identifier" && firstParameter.optional) { - this.raise(TSErrors.SetAccesorCannotHaveOptionalParameter, this.state.curPosition()); + this.raise(TSErrors.SetAccessorCannotHaveOptionalParameter, this.state.curPosition()); } if (firstParameter.type === "RestElement") { - this.raise(TSErrors.SetAccesorCannotHaveRestParameter, this.state.curPosition()); + this.raise(TSErrors.SetAccessorCannotHaveRestParameter, this.state.curPosition()); } } if (method[returnTypeKey]) { - this.raise(TSErrors.SetAccesorCannotHaveReturnType, method[returnTypeKey]); + this.raise(TSErrors.SetAccessorCannotHaveReturnType, method[returnTypeKey]); } } else { method.kind = "method"; @@ -31723,12 +31812,6 @@ var typescript = superClass => class TypeScriptParserMixin extends superClass { this.next(); return this.match(58); } - tsParseMappedTypeParameter() { - const node = this.startNode(); - node.name = this.tsParseTypeParameterName(); - node.constraint = this.tsExpectThenParseType(58); - return this.finishNode(node, "TSTypeParameter"); - } tsParseMappedType() { const node = this.startNode(); this.expect(5); @@ -31740,7 +31823,12 @@ var typescript = superClass => class TypeScriptParserMixin extends superClass { node.readonly = true; } this.expect(0); - node.typeParameter = this.tsParseMappedTypeParameter(); + { + const typeParameter = this.startNode(); + typeParameter.name = this.tsParseTypeParameterName(); + typeParameter.constraint = this.tsExpectThenParseType(58); + node.typeParameter = this.finishNode(typeParameter, "TSTypeParameter"); + } node.nameType = this.eatContextual(93) ? this.tsParseType() : null; this.expect(3); if (this.match(53)) { @@ -32878,7 +32966,7 @@ var typescript = superClass => class TypeScriptParserMixin extends superClass { if (kind !== "const" || !!id.typeAnnotation) { this.raise(TSErrors.InitializerNotAllowedInAmbientContext, init); } else if (!isValidAmbientConstInitializer(init, this.hasPlugin("estree"))) { - this.raise(TSErrors.ConstInitiailizerMustBeStringOrNumericLiteralOrLiteralEnumReference, init); + this.raise(TSErrors.ConstInitializerMustBeStringOrNumericLiteralOrLiteralEnumReference, init); } } return declaration; @@ -33311,15 +33399,21 @@ var typescript = superClass => class TypeScriptParserMixin extends superClass { } } isValidLVal(type, isUnparenthesizedInAssign, binding) { - return getOwn({ - TSTypeCastExpression: true, - TSParameterProperty: "parameter", - TSNonNullExpression: "expression", - TSInstantiationExpression: "expression", - TSAsExpression: (binding !== 64 || !isUnparenthesizedInAssign) && ["expression", true], - TSSatisfiesExpression: (binding !== 64 || !isUnparenthesizedInAssign) && ["expression", true], - TSTypeAssertion: (binding !== 64 || !isUnparenthesizedInAssign) && ["expression", true] - }, type) || super.isValidLVal(type, isUnparenthesizedInAssign, binding); + switch (type) { + case "TSTypeCastExpression": + return true; + case "TSParameterProperty": + return "parameter"; + case "TSNonNullExpression": + case "TSInstantiationExpression": + return "expression"; + case "TSAsExpression": + case "TSSatisfiesExpression": + case "TSTypeAssertion": + return (binding !== 64 || !isUnparenthesizedInAssign) && ["expression", true]; + default: + return super.isValidLVal(type, isUnparenthesizedInAssign, binding); + } } parseBindingAtom() { if (this.state.type === 78) { @@ -33445,12 +33539,17 @@ var typescript = superClass => class TypeScriptParserMixin extends superClass { return param; } tsInAmbientContext(cb) { - const oldIsAmbientContext = this.state.isAmbientContext; + const { + isAmbientContext: oldIsAmbientContext, + strict: oldStrict + } = this.state; this.state.isAmbientContext = true; + this.state.strict = false; try { return cb(); } finally { this.state.isAmbientContext = oldIsAmbientContext; + this.state.strict = oldStrict; } } parseClass(node, isStatement, optionalId) { @@ -33861,107 +33960,71 @@ var v8intrinsic = superClass => class V8IntrinsicMixin extends superClass { return this.parseV8Intrinsic() || super.parseExprAtom(refExpressionErrors); } }; -function hasPlugin(plugins, expectedConfig) { - const [expectedName, expectedOptions] = typeof expectedConfig === "string" ? [expectedConfig, {}] : expectedConfig; - const expectedKeys = Object.keys(expectedOptions); - const expectedOptionsIsEmpty = expectedKeys.length === 0; - return plugins.some(p => { - if (typeof p === "string") { - return expectedOptionsIsEmpty && p === expectedName; - } else { - const [pluginName, pluginOptions] = p; - if (pluginName !== expectedName) { - return false; - } - for (const key of expectedKeys) { - if (pluginOptions[key] !== expectedOptions[key]) { - return false; - } - } - return true; - } - }); -} -function getPluginOption(plugins, name, option) { - const plugin = plugins.find(plugin => { - if (Array.isArray(plugin)) { - return plugin[0] === name; - } else { - return plugin === name; - } - }); - if (plugin && Array.isArray(plugin) && plugin.length > 1) { - return plugin[1][option]; - } - return null; -} const PIPELINE_PROPOSALS = ["minimal", "fsharp", "hack", "smart"]; const TOPIC_TOKENS = ["^^", "@@", "^", "%", "#"]; -function validatePlugins(plugins) { - if (hasPlugin(plugins, "decorators")) { - if (hasPlugin(plugins, "decorators-legacy")) { +function validatePlugins(pluginsMap) { + if (pluginsMap.has("decorators")) { + if (pluginsMap.has("decorators-legacy")) { throw new Error("Cannot use the decorators and decorators-legacy plugin together"); } - const decoratorsBeforeExport = getPluginOption(plugins, "decorators", "decoratorsBeforeExport"); + const decoratorsBeforeExport = pluginsMap.get("decorators").decoratorsBeforeExport; if (decoratorsBeforeExport != null && typeof decoratorsBeforeExport !== "boolean") { throw new Error("'decoratorsBeforeExport' must be a boolean, if specified."); } - const allowCallParenthesized = getPluginOption(plugins, "decorators", "allowCallParenthesized"); + const allowCallParenthesized = pluginsMap.get("decorators").allowCallParenthesized; if (allowCallParenthesized != null && typeof allowCallParenthesized !== "boolean") { throw new Error("'allowCallParenthesized' must be a boolean."); } } - if (hasPlugin(plugins, "flow") && hasPlugin(plugins, "typescript")) { + if (pluginsMap.has("flow") && pluginsMap.has("typescript")) { throw new Error("Cannot combine flow and typescript plugins."); } - if (hasPlugin(plugins, "placeholders") && hasPlugin(plugins, "v8intrinsic")) { + if (pluginsMap.has("placeholders") && pluginsMap.has("v8intrinsic")) { throw new Error("Cannot combine placeholders and v8intrinsic plugins."); } - if (hasPlugin(plugins, "pipelineOperator")) { - const proposal = getPluginOption(plugins, "pipelineOperator", "proposal"); + if (pluginsMap.has("pipelineOperator")) { + var _pluginsMap$get; + const proposal = pluginsMap.get("pipelineOperator").proposal; if (!PIPELINE_PROPOSALS.includes(proposal)) { const proposalList = PIPELINE_PROPOSALS.map(p => `"${p}"`).join(", "); throw new Error(`"pipelineOperator" requires "proposal" option whose value must be one of: ${proposalList}.`); } - const recordAndTupleConfigItem = ["recordAndTuple", { - syntaxType: "hash" - }]; - const tupleSyntaxIsHash = hasPlugin(plugins, recordAndTupleConfigItem); + const tupleSyntaxIsHash = ((_pluginsMap$get = pluginsMap.get("recordAndTuple")) == null ? void 0 : _pluginsMap$get.syntaxType) === "hash"; if (proposal === "hack") { - if (hasPlugin(plugins, "placeholders")) { + if (pluginsMap.has("placeholders")) { throw new Error("Cannot combine placeholders plugin and Hack-style pipes."); } - if (hasPlugin(plugins, "v8intrinsic")) { + if (pluginsMap.has("v8intrinsic")) { throw new Error("Cannot combine v8intrinsic plugin and Hack-style pipes."); } - const topicToken = getPluginOption(plugins, "pipelineOperator", "topicToken"); + const topicToken = pluginsMap.get("pipelineOperator").topicToken; if (!TOPIC_TOKENS.includes(topicToken)) { const tokenList = TOPIC_TOKENS.map(t => `"${t}"`).join(", "); throw new Error(`"pipelineOperator" in "proposal": "hack" mode also requires a "topicToken" option whose value must be one of: ${tokenList}.`); } if (topicToken === "#" && tupleSyntaxIsHash) { - throw new Error(`Plugin conflict between \`["pipelineOperator", { proposal: "hack", topicToken: "#" }]\` and \`${JSON.stringify(recordAndTupleConfigItem)}\`.`); + throw new Error(`Plugin conflict between \`["pipelineOperator", { proposal: "hack", topicToken: "#" }]\` and \`${JSON.stringify(["recordAndTuple", pluginsMap.get("recordAndTuple")])}\`.`); } } else if (proposal === "smart" && tupleSyntaxIsHash) { - throw new Error(`Plugin conflict between \`["pipelineOperator", { proposal: "smart" }]\` and \`${JSON.stringify(recordAndTupleConfigItem)}\`.`); + throw new Error(`Plugin conflict between \`["pipelineOperator", { proposal: "smart" }]\` and \`${JSON.stringify(["recordAndTuple", pluginsMap.get("recordAndTuple")])}\`.`); } } - if (hasPlugin(plugins, "moduleAttributes")) { + if (pluginsMap.has("moduleAttributes")) { { - if (hasPlugin(plugins, "importAssertions") || hasPlugin(plugins, "importAttributes")) { + if (pluginsMap.has("importAttributes") || pluginsMap.has("importAssertions")) { throw new Error("Cannot combine importAssertions, importAttributes and moduleAttributes plugins."); } - const moduleAttributesVersionPluginOption = getPluginOption(plugins, "moduleAttributes", "version"); + const moduleAttributesVersionPluginOption = pluginsMap.get("moduleAttributes").version; if (moduleAttributesVersionPluginOption !== "may-2020") { throw new Error("The 'moduleAttributes' plugin requires a 'version' option," + " representing the last proposal update. Currently, the" + " only supported value is 'may-2020'."); } } } - if (hasPlugin(plugins, "importAssertions") && hasPlugin(plugins, "importAttributes")) { + if (pluginsMap.has("importAttributes") && pluginsMap.has("importAssertions")) { throw new Error("Cannot combine importAssertions and importAttributes plugins."); } - if (hasPlugin(plugins, "recordAndTuple")) { - const syntaxType = getPluginOption(plugins, "recordAndTuple", "syntaxType"); + if (pluginsMap.has("recordAndTuple")) { + const syntaxType = pluginsMap.get("recordAndTuple").syntaxType; if (syntaxType != null) { { const RECORD_AND_TUPLE_SYNTAX_TYPES = ["hash", "bar"]; @@ -33971,12 +34034,12 @@ function validatePlugins(plugins) { } } } - if (hasPlugin(plugins, "asyncDoExpressions") && !hasPlugin(plugins, "doExpressions")) { + if (pluginsMap.has("asyncDoExpressions") && !pluginsMap.has("doExpressions")) { const error = new Error("'asyncDoExpressions' requires 'doExpressions', please add 'doExpressions' to parser plugins."); error.missingPlugins = "doExpressions"; throw error; } - if (hasPlugin(plugins, "optionalChainingAssign") && getPluginOption(plugins, "optionalChainingAssign", "version") !== "2023-07") { + if (pluginsMap.has("optionalChainingAssign") && pluginsMap.get("optionalChainingAssign").version !== "2023-07") { throw new Error("The 'optionalChainingAssign' plugin requires a 'version' option," + " representing the last proposal update. Currently, the" + " only supported value is '2023-07'."); } } @@ -34147,9 +34210,7 @@ class ExpressionParser extends LValParser { } this.next(); node.right = this.parseMaybeAssign(); - this.checkLVal(left, { - in: this.finishNode(node, "AssignmentExpression") - }); + this.checkLVal(left, this.finishNode(node, "AssignmentExpression")); return node; } else if (ownExpressionErrors) { this.checkExpressionErrors(refExpressionErrors, true); @@ -34290,7 +34351,7 @@ class ExpressionParser extends LValParser { parseMaybeUnary(refExpressionErrors, sawUnary) { const startLoc = this.state.startLoc; const isAwait = this.isContextual(96); - if (isAwait && this.isAwaitAllowed()) { + if (isAwait && this.recordAwaitIfAllowed()) { this.next(); const expr = this.parseAwait(startLoc); if (!sawUnary) this.checkExponentialAfterUnary(expr); @@ -34339,9 +34400,7 @@ class ExpressionParser extends LValParser { parseUpdate(node, update, refExpressionErrors) { if (update) { const updateExpressionNode = node; - this.checkLVal(updateExpressionNode.argument, { - in: this.finishNode(updateExpressionNode, "UpdateExpression") - }); + this.checkLVal(updateExpressionNode.argument, this.finishNode(updateExpressionNode, "UpdateExpression")); return node; } const startLoc = this.state.startLoc; @@ -34353,9 +34412,7 @@ class ExpressionParser extends LValParser { node.prefix = false; node.argument = expr; this.next(); - this.checkLVal(expr, { - in: expr = this.finishNode(node, "UpdateExpression") - }); + this.checkLVal(expr, expr = this.finishNode(node, "UpdateExpression")); } return expr; } @@ -34922,10 +34979,12 @@ class ExpressionParser extends LValParser { return this.parseLiteral(value, "DecimalLiteral"); } parseRegExpLiteral(value) { - const node = this.parseLiteral(value.value, "RegExpLiteral"); + const node = this.startNode(); + this.addExtra(node, "raw", this.input.slice(node.start, this.state.end)); node.pattern = value.pattern; node.flags = value.flags; - return node; + this.next(); + return this.finishNode(node, "RegExpLiteral"); } parseBooleanLiteral(value) { const node = this.startNode(); @@ -35425,12 +35484,7 @@ class ExpressionParser extends LValParser { type: "FormalParameters" }; for (const param of node.params) { - this.checkLVal(param, { - in: formalParameters, - binding: 5, - checkClashes, - strictModeChanged - }); + this.checkLVal(param, formalParameters, 5, checkClashes, strictModeChanged); } } parseExprList(close, allowEmpty, refExpressionErrors, nodeForExtra) { @@ -35551,12 +35605,12 @@ class ExpressionParser extends LValParser { } } } - isAwaitAllowed() { - if (this.prodParam.hasAwait) return true; - if (this.options.allowAwaitOutsideFunction && !this.scope.inFunction) { - return true; + recordAwaitIfAllowed() { + const isAwaitAllowed = this.prodParam.hasAwait || this.options.allowAwaitOutsideFunction && !this.scope.inFunction; + if (isAwaitAllowed && !this.scope.inFunction) { + this.state.hasTopLevelAwait = true; } - return false; + return isAwaitAllowed; } parseAwait(startLoc) { const node = this.startNodeAt(startLoc); @@ -35895,12 +35949,15 @@ class StatementParser extends ExpressionParser { program.sourceType = sourceType; program.interpreter = this.parseInterpreterDirective(); this.parseBlockBody(program, true, true, end); - if (this.inModule && !this.options.allowUndeclaredExports && this.scope.undefinedExports.size > 0) { - for (const [localName, at] of Array.from(this.scope.undefinedExports)) { - this.raise(Errors.ModuleExportUndefined, at, { - localName - }); + if (this.inModule) { + if (!this.options.allowUndeclaredExports && this.scope.undefinedExports.size > 0) { + for (const [localName, at] of Array.from(this.scope.undefinedExports)) { + this.raise(Errors.ModuleExportUndefined, at, { + localName + }); + } } + this.addExtra(program, "topLevelAwait", this.state.hasTopLevelAwait); } let finishedProgram; if (end === 139) { @@ -35964,10 +36021,10 @@ class StatementParser extends ExpressionParser { const nextCh = this.codePointAtPos(next); return this.chStartsBindingPattern(nextCh) || this.chStartsBindingIdentifier(nextCh, next); } - hasInLineFollowingBindingIdentifier() { + hasInLineFollowingBindingIdentifierOrBrace() { const next = this.nextTokenInLineStart(); const nextCh = this.codePointAtPos(next); - return this.chStartsBindingIdentifier(nextCh, next); + return nextCh === 123 || this.chStartsBindingIdentifier(nextCh, next); } startsUsingForOf() { const { @@ -36020,12 +36077,12 @@ class StatementParser extends ExpressionParser { return this.parseStatementContent(flags, decorators); } parseStatementContent(flags, decorators) { - const starttype = this.state.type; + const startType = this.state.type; const node = this.startNode(); const allowDeclaration = !!(flags & 2); const allowFunctionDeclaration = !!(flags & 4); const topLevel = flags & 1; - switch (starttype) { + switch (startType) { case 60: return this.parseBreakContinueStatement(node, true); case 63: @@ -36057,7 +36114,7 @@ class StatementParser extends ExpressionParser { return this.parseTryStatement(node); case 96: if (!this.state.containsEsc && this.startsAwaitUsing()) { - if (!this.isAwaitAllowed()) { + if (!this.recordAwaitIfAllowed()) { this.raise(Errors.AwaitUsingNotInAsyncContext, node); } else if (!allowDeclaration) { this.raise(Errors.UnexpectedLexicalDeclaration, node); @@ -36067,7 +36124,7 @@ class StatementParser extends ExpressionParser { } break; case 107: - if (this.state.containsEsc || !this.hasInLineFollowingBindingIdentifier()) { + if (this.state.containsEsc || !this.hasInLineFollowingBindingIdentifierOrBrace()) { break; } this.expectPlugin("explicitResourceManagement"); @@ -36124,7 +36181,7 @@ class StatementParser extends ExpressionParser { } this.next(); let result; - if (starttype === 83) { + if (startType === 83) { result = this.parseImport(node); if (result.type === "ImportDeclaration" && (!result.importKind || result.importKind === "value")) { this.sawUnambiguousESM = true; @@ -36151,7 +36208,7 @@ class StatementParser extends ExpressionParser { } const maybeName = this.state.value; const expr = this.parseExpression(); - if (tokenIsIdentifier(starttype) && expr.type === "Identifier" && this.eat(14)) { + if (tokenIsIdentifier(startType) && expr.type === "Identifier" && this.eat(14)) { return this.parseLabeledStatement(node, maybeName, expr, flags); } else { return this.parseExpressionStatement(node, expr, decorators); @@ -36304,8 +36361,9 @@ class StatementParser extends ExpressionParser { this.next(); this.state.labels.push(loopLabel); let awaitAt = null; - if (this.isAwaitAllowed() && this.eatContextual(96)) { - awaitAt = this.state.lastTokStartLoc; + if (this.isContextual(96) && this.recordAwaitIfAllowed()) { + awaitAt = this.state.startLoc; + this.next(); } this.scope.enter(0); this.expect(10); @@ -36325,7 +36383,7 @@ class StatementParser extends ExpressionParser { let kind; if (startsWithAwaitUsing) { kind = "await using"; - if (!this.isAwaitAllowed()) { + if (!this.recordAwaitIfAllowed()) { this.raise(Errors.AwaitUsingNotInAsyncContext, this.state.startLoc); } this.next(); @@ -36365,9 +36423,7 @@ class StatementParser extends ExpressionParser { this.toAssignable(init, true); const type = isForOf ? "ForOfStatement" : "ForInStatement"; this.checkLVal(init, { - in: { - type - } + type }); return this.parseForIn(node, init, awaitAt); } else { @@ -36454,11 +36510,8 @@ class StatementParser extends ExpressionParser { const param = this.parseBindingAtom(); this.scope.enter(this.options.annexB && param.type === "Identifier" ? 8 : 0); this.checkLVal(param, { - in: { - type: "CatchClause" - }, - binding: 9 - }); + type: "CatchClause" + }, 9); return param; } parseTryStatement(node) { @@ -36661,12 +36714,14 @@ class StatementParser extends ExpressionParser { } parseVarId(decl, kind) { const id = this.parseBindingAtom(); + if (kind === "using" || kind === "await using") { + if (id.type === "ArrayPattern" || id.type === "ObjectPattern") { + this.raise(Errors.UsingDeclarationHasBindingPattern, id.loc.start); + } + } this.checkLVal(id, { - in: { - type: "VariableDeclarator" - }, - binding: kind === "var" ? 5 : 8201 - }); + type: "VariableDeclarator" + }, kind === "var" ? 5 : 8201); decl.id = id; } parseAsyncFunctionExpression(node) { @@ -37360,7 +37415,7 @@ class StatementParser extends ExpressionParser { parseModuleExportName() { if (this.match(133)) { const result = this.parseStringLiteral(this.state.value); - const surrogate = result.value.match(loneSurrogate); + const surrogate = loneSurrogate.exec(result.value); if (surrogate) { this.raise(Errors.ModuleExportNameHasLoneSurrogate, result, { surrogateCharCode: surrogate[0].charCodeAt(0) @@ -37511,11 +37566,8 @@ class StatementParser extends ExpressionParser { } finishImportSpecifier(specifier, type, bindingType = 8201) { this.checkLVal(specifier.local, { - in: { - type - }, - binding: bindingType - }); + type + }, bindingType); return this.finishNode(specifier, type); } parseImportAttributes() { @@ -37681,12 +37733,12 @@ class StatementParser extends ExpressionParser { } } class Parser extends StatementParser { - constructor(options, input) { + constructor(options, input, pluginsMap) { options = getOptions(options); super(options, input); this.options = options; this.initializeScopes(); - this.plugins = pluginsMap(this.options.plugins); + this.plugins = pluginsMap; this.filename = options.sourceFilename; } getScopeHandler() { @@ -37704,14 +37756,6 @@ class Parser extends StatementParser { return file; } } -function pluginsMap(plugins) { - const pluginMap = new Map(); - for (const plugin of plugins) { - const [name, options] = Array.isArray(plugin) ? plugin : [plugin, {}]; - if (!pluginMap.has(name)) pluginMap.set(name, options || {}); - } - return pluginMap; -} function parse(input, options) { var _options; if (((_options = options) == null ? void 0 : _options.sourceType) === "unambiguous") { @@ -37760,23 +37804,40 @@ function generateExportedTokenTypes(internalTokenTypes) { const tokTypes = generateExportedTokenTypes(tt); function getParser(options, input) { let cls = Parser; + const pluginsMap = new Map(); if (options != null && options.plugins) { - validatePlugins(options.plugins); - cls = getParserClass(options.plugins); + for (const plugin of options.plugins) { + let name, opts; + if (typeof plugin === "string") { + name = plugin; + } else { + [name, opts] = plugin; + } + if (!pluginsMap.has(name)) { + pluginsMap.set(name, opts || {}); + } + } + validatePlugins(pluginsMap); + cls = getParserClass(pluginsMap); } - return new cls(options, input); + return new cls(options, input, pluginsMap); } -const parserClassCache = {}; -function getParserClass(pluginsFromOptions) { - const pluginList = mixinPluginNames.filter(name => hasPlugin(pluginsFromOptions, name)); - const key = pluginList.join("/"); - let cls = parserClassCache[key]; +const parserClassCache = new Map(); +function getParserClass(pluginsMap) { + const pluginList = []; + for (const name of mixinPluginNames) { + if (pluginsMap.has(name)) { + pluginList.push(name); + } + } + const key = pluginList.join("|"); + let cls = parserClassCache.get(key); if (!cls) { cls = Parser; for (const plugin of pluginList) { cls = mixinPlugins[plugin](cls); } - parserClassCache[key] = cls; + parserClassCache.set(key, cls); } return cls; } @@ -38395,9 +38456,24 @@ function applyReplacement(placeholder, ast, replacement) { throw new Error("Cannot replace single expression with an array."); } } + function set(parent, key, value) { + const node = parent[key]; + parent[key] = value; + if (node.type === "Identifier") { + if (node.typeAnnotation) { + value.typeAnnotation = node.typeAnnotation; + } + if (node.optional) { + value.optional = node.optional; + } + if (node.decorators) { + value.decorators = node.decorators; + } + } + } if (index === undefined) { validate(parent, key, replacement); - parent[key] = replacement; + set(parent, key, replacement); } else { const items = parent[key].slice(); if (placeholder.type === "statement" || placeholder.type === "param") { @@ -38406,10 +38482,10 @@ function applyReplacement(placeholder, ast, replacement) { } else if (Array.isArray(replacement)) { items.splice(index, 1, ...replacement); } else { - items[index] = replacement; + set(items, index, replacement); } } else { - items[index] = replacement; + set(items, index, replacement); } validate(parent, key, items); parent[key] = items; @@ -48952,6 +49028,9 @@ function cloneNodeInternal(node, deep = true, withoutLoc = false, commentsCache) if (hasOwn(node, "typeAnnotation")) { newNode.typeAnnotation = deep ? cloneIfNodeOrArray(node.typeAnnotation, true, withoutLoc, commentsCache) : node.typeAnnotation; } + if (hasOwn(node, "decorators")) { + newNode.decorators = deep ? cloneIfNodeOrArray(node.decorators, true, withoutLoc, commentsCache) : node.decorators; + } } else if (!hasOwn(_index.NODE_FIELDS, type)) { throw new Error(`Unknown node type: "${type}"`); } else { @@ -49727,7 +49806,7 @@ function valueToNode(value) { } if (isRegExp(value)) { const pattern = value.source; - const flags = value.toString().match(/\/([a-z]+|)$/)[1]; + const flags = /\/([a-z]*)$/.exec(value.toString())[1]; return (0, _index.regExpLiteral)(pattern, flags); } if (Array.isArray(value)) { @@ -49950,7 +50029,8 @@ defineType("DebuggerStatement", { aliases: ["Statement"] }); defineType("DoWhileStatement", { - visitor: ["test", "body"], + builder: ["test", "body"], + visitor: ["body", "test"], fields: { test: { validate: (0, _utils.assertNodeType)("Expression") @@ -50068,7 +50148,7 @@ const functionDeclarationCommon = () => Object.assign({}, functionCommon(), { exports.functionDeclarationCommon = functionDeclarationCommon; defineType("FunctionDeclaration", { builder: ["id", "params", "body", "generator", "async"], - visitor: ["id", "params", "body", "returnType", "typeParameters"], + visitor: ["id", "typeParameters", "params", "returnType", "body"], fields: Object.assign({}, functionDeclarationCommon(), functionTypeAnnotationCommon(), { body: { validate: (0, _utils.assertNodeType)("BlockStatement") @@ -50338,6 +50418,7 @@ defineType("ObjectExpression", { }); defineType("ObjectMethod", { builder: ["kind", "key", "params", "body", "computed", "generator", "async"], + visitor: ["decorators", "key", "typeParameters", "params", "returnType", "body"], fields: Object.assign({}, functionCommon(), functionTypeAnnotationCommon(), { kind: Object.assign({ validate: (0, _utils.assertOneOf)("method", "get", "set") @@ -50367,7 +50448,6 @@ defineType("ObjectMethod", { validate: (0, _utils.assertNodeType)("BlockStatement") } }), - visitor: ["key", "params", "body", "decorators", "returnType", "typeParameters"], aliases: ["UserWhitespacable", "Function", "Scopable", "BlockParent", "FunctionParent", "Method", "ObjectMember"] }); defineType("ObjectProperty", { @@ -50670,7 +50750,7 @@ defineType("ArrayPattern", { }); defineType("ArrowFunctionExpression", { builder: ["params", "body", "async"], - visitor: ["params", "body", "returnType", "typeParameters"], + visitor: ["typeParameters", "params", "returnType", "body"], aliases: ["Scopable", "Function", "BlockParent", "FunctionParent", "Expression", "Pureish"], fields: Object.assign({}, functionCommon(), functionTypeAnnotationCommon(), { expression: { @@ -50695,7 +50775,7 @@ defineType("ClassBody", { }); defineType("ClassExpression", { builder: ["id", "superClass", "body", "decorators"], - visitor: ["id", "body", "superClass", "mixins", "typeParameters", "superTypeParameters", "implements", "decorators"], + visitor: ["decorators", "id", "typeParameters", "superClass", "superTypeParameters", "mixins", "implements", "body"], aliases: ["Scopable", "Class", "Expression"], fields: { id: { @@ -50962,7 +51042,8 @@ defineType("ImportNamespaceSpecifier", { } }); defineType("ImportSpecifier", { - visitor: ["local", "imported"], + visitor: ["imported", "local"], + builder: ["local", "imported"], aliases: ["ModuleSpecifier"], fields: { local: { @@ -51082,7 +51163,7 @@ exports.classMethodOrDeclareMethodCommon = classMethodOrDeclareMethodCommon; defineType("ClassMethod", { aliases: ["Function", "Scopable", "BlockParent", "FunctionParent", "Method"], builder: ["kind", "key", "params", "body", "computed", "static", "generator", "async"], - visitor: ["key", "params", "body", "decorators", "returnType", "typeParameters"], + visitor: ["decorators", "key", "typeParameters", "params", "returnType", "body"], fields: Object.assign({}, classMethodOrDeclareMethodCommon(), functionTypeAnnotationCommon(), { body: { validate: (0, _utils.assertNodeType)("BlockStatement") @@ -51113,7 +51194,7 @@ defineType("Super", { aliases: ["Expression"] }); defineType("TaggedTemplateExpression", { - visitor: ["tag", "quasi", "typeParameters"], + visitor: ["tag", "typeParameters", "quasi"], builder: ["tag", "quasi"], aliases: ["Expression"], fields: { @@ -51293,7 +51374,7 @@ defineType("OptionalCallExpression", { } }); defineType("ClassProperty", { - visitor: ["key", "value", "typeAnnotation", "decorators"], + visitor: ["decorators", "key", "typeAnnotation", "value"], builder: ["key", "value", "typeAnnotation", "decorators", "computed", "static"], aliases: ["Property"], fields: Object.assign({}, classMethodOrPropertyCommon(), { @@ -51328,7 +51409,7 @@ defineType("ClassProperty", { }) }); defineType("ClassAccessorProperty", { - visitor: ["key", "value", "typeAnnotation", "decorators"], + visitor: ["decorators", "key", "typeAnnotation", "value"], builder: ["key", "value", "typeAnnotation", "decorators", "computed", "static"], aliases: ["Property", "Accessor"], fields: Object.assign({}, classMethodOrPropertyCommon(), { @@ -51373,7 +51454,7 @@ defineType("ClassAccessorProperty", { }) }); defineType("ClassPrivateProperty", { - visitor: ["key", "value", "decorators", "typeAnnotation"], + visitor: ["decorators", "key", "typeAnnotation", "value"], builder: ["key", "value", "decorators", "static"], aliases: ["Property", "Private"], fields: { @@ -51412,7 +51493,7 @@ defineType("ClassPrivateProperty", { }); defineType("ClassPrivateMethod", { builder: ["kind", "key", "params", "body", "static"], - visitor: ["key", "params", "body", "decorators", "returnType", "typeParameters"], + visitor: ["decorators", "key", "typeParameters", "params", "returnType", "body"], aliases: ["Function", "Scopable", "BlockParent", "FunctionParent", "Method", "Private"], fields: Object.assign({}, classMethodOrDeclareMethodCommon(), functionTypeAnnotationCommon(), { kind: { @@ -51854,7 +51935,8 @@ defineType("ObjectTypeAnnotation", { } }); defineType("ObjectTypeInternalSlot", { - visitor: ["id", "value", "optional", "static", "method"], + visitor: ["id", "value"], + builder: ["id", "value", "optional", "static", "method"], aliases: ["UserWhitespacable"], fields: { id: (0, _utils.validateType)("Identifier"), @@ -51873,7 +51955,8 @@ defineType("ObjectTypeCallProperty", { } }); defineType("ObjectTypeIndexer", { - visitor: ["id", "key", "value", "variance"], + visitor: ["variance", "id", "key", "value"], + builder: ["id", "key", "value", "variance"], aliases: ["UserWhitespacable"], fields: { id: (0, _utils.validateOptionalType)("Identifier"), @@ -51915,7 +51998,8 @@ defineType("OpaqueType", { } }); defineType("QualifiedTypeIdentifier", { - visitor: ["id", "qualification"], + visitor: ["qualification", "id"], + builder: ["id", "qualification"], fields: { id: (0, _utils.validateType)("Identifier"), qualification: (0, _utils.validateType)(["Identifier", "QualifiedTypeIdentifier"]) @@ -52714,14 +52798,16 @@ defineType("TSIndexedAccessType", { }); defineType("TSMappedType", { aliases: ["TSType"], - visitor: ["typeParameter", "typeAnnotation", "nameType"], - fields: { + visitor: ["typeParameter", "nameType", "typeAnnotation"], + builder: ["typeParameter", "typeAnnotation", "nameType"], + fields: Object.assign({}, { + typeParameter: (0, _utils.validateType)("TSTypeParameter") + }, { readonly: (0, _utils.validateOptional)((0, _utils.assertOneOf)(true, false, "+", "-")), - typeParameter: (0, _utils.validateType)("TSTypeParameter"), optional: (0, _utils.validateOptional)((0, _utils.assertOneOf)(true, false, "+", "-")), typeAnnotation: (0, _utils.validateOptionalType)("TSType"), nameType: (0, _utils.validateOptionalType)("TSType") - } + }) }); defineType("TSLiteralType", { aliases: ["TSType", "TSBaseType"], @@ -53047,7 +53133,7 @@ function assertEach(callback) { } function assertOneOf(...values) { function validate(node, key, val) { - if (values.indexOf(val) < 0) { + if (!values.includes(val)) { throw new TypeError(`Property ${key} expected value to be one of ${JSON.stringify(values)} but got ${JSON.stringify(val)}`); } } @@ -53190,7 +53276,7 @@ function defineType(type, opts = {}) { const aliases = opts.aliases || inherits.aliases || []; const builder = opts.builder || inherits.builder || opts.visitor || []; for (const k of Object.keys(opts)) { - if (validTypeOpts.indexOf(k) === -1) { + if (!validTypeOpts.includes(k)) { throw new Error(`Unknown type option "${k}" on ${type}`); } } @@ -53202,7 +53288,7 @@ function defineType(type, opts = {}) { } for (const key of Object.keys(fields)) { const field = fields[key]; - if (field.default !== undefined && builder.indexOf(key) === -1) { + if (field.default !== undefined && !builder.includes(key)) { field.optional = true; } if (field.default === undefined) { @@ -53211,7 +53297,7 @@ function defineType(type, opts = {}) { field.validate = assertValueType(getType(field.default)); } for (const k of Object.keys(field)) { - if (validFieldKeys.indexOf(k) === -1) { + if (!validFieldKeys.includes(k)) { throw new Error(`Unknown field key "${k}" on ${type}.${key}`); } } @@ -53278,8 +53364,10 @@ var _exportNames = { removeProperties: true, removePropertiesDeep: true, removeTypeDuplicates: true, + getAssignmentIdentifiers: true, getBindingIdentifiers: true, getOuterBindingIdentifiers: true, + getFunctionName: true, traverse: true, traverseFast: true, shallowEqual: true, @@ -53399,12 +53487,24 @@ Object.defineProperty(exports, "ensureBlock", ({ return _ensureBlock.default; } })); +Object.defineProperty(exports, "getAssignmentIdentifiers", ({ + enumerable: true, + get: function () { + return _getAssignmentIdentifiers.default; + } +})); Object.defineProperty(exports, "getBindingIdentifiers", ({ enumerable: true, get: function () { return _getBindingIdentifiers.default; } })); +Object.defineProperty(exports, "getFunctionName", ({ + enumerable: true, + get: function () { + return _getFunctionName.default; + } +})); Object.defineProperty(exports, "getOuterBindingIdentifiers", ({ enumerable: true, get: function () { @@ -53758,8 +53858,10 @@ var _prependToMemberExpression = __nccwpck_require__(6676); var _removeProperties = __nccwpck_require__(7517); var _removePropertiesDeep = __nccwpck_require__(665); var _removeTypeDuplicates = __nccwpck_require__(1009); +var _getAssignmentIdentifiers = __nccwpck_require__(2901); var _getBindingIdentifiers = __nccwpck_require__(3331); var _getOuterBindingIdentifiers = __nccwpck_require__(3102); +var _getFunctionName = __nccwpck_require__(3591); var _traverse = __nccwpck_require__(9018); Object.keys(_traverse).forEach(function (key) { if (key === "default" || key === "__esModule") return; @@ -53865,7 +53967,7 @@ function removeTypeDuplicates(nodesIn) { for (let i = 0; i < nodes.length; i++) { const node = nodes[i]; if (!node) continue; - if (types.indexOf(node) >= 0) { + if (types.includes(node)) { continue; } if ((0, _index.isAnyTypeAnnotation)(node)) { @@ -54053,7 +54155,7 @@ function removeTypeDuplicates(nodesIn) { for (let i = 0; i < nodes.length; i++) { const node = nodes[i]; if (!node) continue; - if (types.indexOf(node) >= 0) { + if (types.includes(node)) { continue; } if ((0, _index.isTSAnyKeyword)(node)) { @@ -54101,6 +54203,62 @@ function removeTypeDuplicates(nodesIn) { //# sourceMappingURL=removeTypeDuplicates.js.map +/***/ }), + +/***/ 2901: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = getAssignmentIdentifiers; +function getAssignmentIdentifiers(node) { + const search = [].concat(node); + const ids = Object.create(null); + while (search.length) { + const id = search.pop(); + if (!id) continue; + switch (id.type) { + case "ArrayPattern": + search.push(...id.elements); + break; + case "AssignmentExpression": + case "AssignmentPattern": + case "ForInStatement": + case "ForOfStatement": + search.push(id.left); + break; + case "ObjectPattern": + search.push(...id.properties); + break; + case "ObjectProperty": + search.push(id.value); + break; + case "RestElement": + case "UpdateExpression": + search.push(id.argument); + break; + case "UnaryExpression": + if (id.operator === "delete") { + search.push(id.argument); + } + break; + case "Identifier": + ids[id.name] = id; + break; + default: + break; + } + } + return ids; +} + +//# sourceMappingURL=getAssignmentIdentifiers.js.map + + /***/ }), /***/ 3331: @@ -54153,14 +54311,18 @@ function getBindingIdentifiers(node, duplicates, outerOnly, newBindingsOnly) { const key = keys[i]; const nodes = id[key]; if (nodes) { - Array.isArray(nodes) ? search.push(...nodes) : search.push(nodes); + if (Array.isArray(nodes)) { + search.push(...nodes); + } else { + search.push(nodes); + } } } } } return ids; } -getBindingIdentifiers.keys = { +const keys = { DeclareClass: ["id"], DeclareFunction: ["id"], DeclareModule: ["id"], @@ -54201,10 +54363,82 @@ getBindingIdentifiers.keys = { VariableDeclaration: ["declarations"], VariableDeclarator: ["id"] }; +getBindingIdentifiers.keys = keys; //# sourceMappingURL=getBindingIdentifiers.js.map +/***/ }), + +/***/ 3591: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = getFunctionName; +var _index = __nccwpck_require__(2605); +function getNameFromLiteralId(id) { + if ((0, _index.isNullLiteral)(id)) { + return "null"; + } + if ((0, _index.isRegExpLiteral)(id)) { + return `/${id.pattern}/${id.flags}`; + } + if ((0, _index.isTemplateLiteral)(id)) { + return id.quasis.map(quasi => quasi.value.raw).join(""); + } + if (id.value !== undefined) { + return String(id.value); + } + return null; +} +function getObjectMemberKey(node) { + if (!node.computed || (0, _index.isLiteral)(node.key)) { + return node.key; + } +} +function getFunctionName(node, parent) { + if ("id" in node && node.id) { + return { + name: node.id.name, + originalNode: node.id + }; + } + let prefix = ""; + let id; + if ((0, _index.isObjectProperty)(parent, { + value: node + })) { + id = getObjectMemberKey(parent); + } else if ((0, _index.isObjectMethod)(node) || (0, _index.isClassMethod)(node)) { + id = getObjectMemberKey(node); + if (node.kind === "get") prefix = "get ";else if (node.kind === "set") prefix = "set "; + } else if ((0, _index.isVariableDeclarator)(parent, { + init: node + })) { + id = parent.id; + } else if ((0, _index.isAssignmentExpression)(parent, { + operator: "=", + right: node + })) { + id = parent.left; + } + if (!id) return null; + const name = (0, _index.isLiteral)(id) ? getNameFromLiteralId(id) : (0, _index.isIdentifier)(id) ? id.name : (0, _index.isPrivateName)(id) ? id.id.name : null; + if (name == null) return null; + return { + name: prefix + name, + originalNode: id + }; +} + +//# sourceMappingURL=getFunctionName.js.map + + /***/ }), /***/ 3102: @@ -54409,7 +54643,7 @@ function cleanJSXElementLiteralChild(child, args) { const lines = child.value.split(/\r\n|\n|\r/); let lastNonEmptyLine = 0; for (let i = 0; i < lines.length; i++) { - if (lines[i].match(/[^ \t]/)) { + if (/[^ \t]/.exec(lines[i])) { lastNonEmptyLine = i; } } @@ -54421,10 +54655,10 @@ function cleanJSXElementLiteralChild(child, args) { const isLastNonEmptyLine = i === lastNonEmptyLine; let trimmedLine = line.replace(/\t/g, " "); if (!isFirstLine) { - trimmedLine = trimmedLine.replace(/^[ ]+/, ""); + trimmedLine = trimmedLine.replace(/^ +/, ""); } if (!isLastLine) { - trimmedLine = trimmedLine.replace(/[ ]+$/, ""); + trimmedLine = trimmedLine.replace(/ +$/, ""); } if (trimmedLine) { if (!isLastNonEmptyLine) { @@ -57303,7 +57537,7 @@ function isBinding(node, parent, grandparent) { const key = keys[i]; const val = parent[key]; if (Array.isArray(val)) { - if (val.indexOf(node) >= 0) return true; + if (val.includes(node)) return true; } else { if (val === node) return true; } diff --git a/.github/actions/javascript/bumpVersion/index.js b/.github/actions/javascript/bumpVersion/index.js index c8360931845a..43bd09558c26 100644 --- a/.github/actions/javascript/bumpVersion/index.js +++ b/.github/actions/javascript/bumpVersion/index.js @@ -1928,7 +1928,7 @@ class SemVer { do { const a = this.build[i] const b = other.build[i] - debug('build compare', i, a, b) + debug('prerelease compare', i, a, b) if (a === undefined && b === undefined) { return 0 } else if (b === undefined) { @@ -2154,10 +2154,6 @@ const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || // Max safe segment length for coercion. const MAX_SAFE_COMPONENT_LENGTH = 16 -// Max safe length for a build identifier. The max length minus 6 characters for -// the shortest version with a build 0.0.0+BUILD. -const MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6 - const RELEASE_TYPES = [ 'major', 'premajor', @@ -2171,7 +2167,6 @@ const RELEASE_TYPES = [ module.exports = { MAX_LENGTH, MAX_SAFE_COMPONENT_LENGTH, - MAX_SAFE_BUILD_LENGTH, MAX_SAFE_INTEGER, RELEASE_TYPES, SEMVER_SPEC_VERSION, @@ -2253,11 +2248,7 @@ module.exports = parseOptions /***/ 9523: /***/ ((module, exports, __nccwpck_require__) => { -const { - MAX_SAFE_COMPONENT_LENGTH, - MAX_SAFE_BUILD_LENGTH, - MAX_LENGTH, -} = __nccwpck_require__(2293) +const { MAX_SAFE_COMPONENT_LENGTH } = __nccwpck_require__(2293) const debug = __nccwpck_require__(427) exports = module.exports = {} @@ -2268,31 +2259,16 @@ const src = exports.src = [] const t = exports.t = {} let R = 0 -const LETTERDASHNUMBER = '[a-zA-Z0-9-]' - -// Replace some greedy regex tokens to prevent regex dos issues. These regex are -// used internally via the safeRe object since all inputs in this library get -// normalized first to trim and collapse all extra whitespace. The original -// regexes are exported for userland consumption and lower level usage. A -// future breaking change could export the safer regex only with a note that -// all input should have extra whitespace removed. -const safeRegexReplacements = [ - ['\\s', 1], - ['\\d', MAX_LENGTH], - [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH], -] - -const makeSafeRegex = (value) => { - for (const [token, max] of safeRegexReplacements) { - value = value - .split(`${token}*`).join(`${token}{0,${max}}`) - .split(`${token}+`).join(`${token}{1,${max}}`) - } - return value -} - const createToken = (name, value, isGlobal) => { - const safe = makeSafeRegex(value) + // Replace all greedy whitespace to prevent regex dos issues. These regex are + // used internally via the safeRe object since all inputs in this library get + // normalized first to trim and collapse all extra whitespace. The original + // regexes are exported for userland consumption and lower level usage. A + // future breaking change could export the safer regex only with a note that + // all input should have extra whitespace removed. + const safe = value + .split('\\s*').join('\\s{0,1}') + .split('\\s+').join('\\s') const index = R++ debug(name, index, value) t[name] = index @@ -2308,13 +2284,13 @@ const createToken = (name, value, isGlobal) => { // A single `0`, or a non-zero digit followed by zero or more digits. createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*') -createToken('NUMERICIDENTIFIERLOOSE', '\\d+') +createToken('NUMERICIDENTIFIERLOOSE', '[0-9]+') // ## Non-numeric Identifier // Zero or more digits, followed by a letter or hyphen, and then zero or // more letters, digits, or hyphens. -createToken('NONNUMERICIDENTIFIER', `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`) +createToken('NONNUMERICIDENTIFIER', '\\d*[a-zA-Z-][a-zA-Z0-9-]*') // ## Main Version // Three dot-separated numeric identifiers. @@ -2349,7 +2325,7 @@ createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE] // ## Build Metadata Identifier // Any combination of digits, letters, or hyphens. -createToken('BUILDIDENTIFIER', `${LETTERDASHNUMBER}+`) +createToken('BUILDIDENTIFIER', '[0-9A-Za-z-]+') // ## Build Metadata // Plus sign, followed by one or more period-separated build metadata @@ -2409,17 +2385,12 @@ createToken('XRANGELOOSE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAINLOOSE]}$`) // Coercion. // Extract anything that could conceivably be a part of a valid semver -createToken('COERCEPLAIN', `${'(^|[^\\d])' + +createToken('COERCE', `${'(^|[^\\d])' + '(\\d{1,'}${MAX_SAFE_COMPONENT_LENGTH}})` + `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` + - `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?`) -createToken('COERCE', `${src[t.COERCEPLAIN]}(?:$|[^\\d])`) -createToken('COERCEFULL', src[t.COERCEPLAIN] + - `(?:${src[t.PRERELEASE]})?` + - `(?:${src[t.BUILD]})?` + + `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` + `(?:$|[^\\d])`) createToken('COERCERTL', src[t.COERCE], true) -createToken('COERCERTLFULL', src[t.COERCEFULL], true) // Tilde ranges. // Meaning is "reasonably at or greater than" diff --git a/.github/actions/javascript/checkReactCompiler/action.yml b/.github/actions/javascript/checkReactCompiler/action.yml deleted file mode 100644 index a8a1c35744c3..000000000000 --- a/.github/actions/javascript/checkReactCompiler/action.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: 'Check React compiler' -description: 'Compares two lists of compiled files and fails a job if previously successfully compiled files are no longer compiled successfully' -inputs: - OLD_LIST: - description: List of compiled files from the previous commit - required: true - NEW_LIST: - description: List of compiled files from the current commit - required: true -runs: - using: 'node20' - main: 'index.js' diff --git a/.github/actions/javascript/checkReactCompiler/checkReactCompiler.ts b/.github/actions/javascript/checkReactCompiler/checkReactCompiler.ts deleted file mode 100644 index fbc4b249cb46..000000000000 --- a/.github/actions/javascript/checkReactCompiler/checkReactCompiler.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -import * as core from '@actions/core'; - -type ReactCompilerOutput = { - success: string[]; - failure: string[]; -}; - -const run = function (): Promise { - const oldList = JSON.parse(core.getInput('OLD_LIST', {required: true})) as ReactCompilerOutput; - const newList = JSON.parse(core.getInput('NEW_LIST', {required: true})) as ReactCompilerOutput; - - const errors: string[] = []; - - oldList.success.forEach((file) => { - if (newList.success.includes(file) || !newList.failure.includes(file)) { - return; - } - - errors.push(file); - }); - - if (errors.length > 0) { - errors.forEach((error) => console.error(error)); - throw new Error( - 'Some files could be compiled with react-compiler before successfully, but now they can not be compiled. Check https://github.com/Expensify/App/blob/main/contributingGuides/REACT_COMPILER.md documentation to see how you can fix this.', - ); - } - - return Promise.resolve(); -}; - -if (require.main === module) { - run(); -} - -export default run; diff --git a/.github/actions/javascript/checkReactCompiler/index.js b/.github/actions/javascript/checkReactCompiler/index.js deleted file mode 100644 index 1d8ca6adbd16..000000000000 --- a/.github/actions/javascript/checkReactCompiler/index.js +++ /dev/null @@ -1,2883 +0,0 @@ -/** - * NOTE: This is a compiled file. DO NOT directly edit this file. - */ -/******/ (() => { // webpackBootstrap -/******/ var __webpack_modules__ = ({ - -/***/ 351: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.issue = exports.issueCommand = void 0; -const os = __importStar(__nccwpck_require__(37)); -const utils_1 = __nccwpck_require__(278); -/** - * Commands - * - * Command Format: - * ::name key=value,key=value::message - * - * Examples: - * ::warning::This is the message - * ::set-env name=MY_VAR::some value - */ -function issueCommand(command, properties, message) { - const cmd = new Command(command, properties, message); - process.stdout.write(cmd.toString() + os.EOL); -} -exports.issueCommand = issueCommand; -function issue(name, message = '') { - issueCommand(name, {}, message); -} -exports.issue = issue; -const CMD_STRING = '::'; -class Command { - constructor(command, properties, message) { - if (!command) { - command = 'missing.command'; - } - this.command = command; - this.properties = properties; - this.message = message; - } - toString() { - let cmdStr = CMD_STRING + this.command; - if (this.properties && Object.keys(this.properties).length > 0) { - cmdStr += ' '; - let first = true; - for (const key in this.properties) { - if (this.properties.hasOwnProperty(key)) { - const val = this.properties[key]; - if (val) { - if (first) { - first = false; - } - else { - cmdStr += ','; - } - cmdStr += `${key}=${escapeProperty(val)}`; - } - } - } - } - cmdStr += `${CMD_STRING}${escapeData(this.message)}`; - return cmdStr; - } -} -function escapeData(s) { - return utils_1.toCommandValue(s) - .replace(/%/g, '%25') - .replace(/\r/g, '%0D') - .replace(/\n/g, '%0A'); -} -function escapeProperty(s) { - return utils_1.toCommandValue(s) - .replace(/%/g, '%25') - .replace(/\r/g, '%0D') - .replace(/\n/g, '%0A') - .replace(/:/g, '%3A') - .replace(/,/g, '%2C'); -} -//# sourceMappingURL=command.js.map - -/***/ }), - -/***/ 186: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.getIDToken = exports.getState = exports.saveState = exports.group = exports.endGroup = exports.startGroup = exports.info = exports.notice = exports.warning = exports.error = exports.debug = exports.isDebug = exports.setFailed = exports.setCommandEcho = exports.setOutput = exports.getBooleanInput = exports.getMultilineInput = exports.getInput = exports.addPath = exports.setSecret = exports.exportVariable = exports.ExitCode = void 0; -const command_1 = __nccwpck_require__(351); -const file_command_1 = __nccwpck_require__(717); -const utils_1 = __nccwpck_require__(278); -const os = __importStar(__nccwpck_require__(37)); -const path = __importStar(__nccwpck_require__(17)); -const oidc_utils_1 = __nccwpck_require__(41); -/** - * The code to exit an action - */ -var ExitCode; -(function (ExitCode) { - /** - * A code indicating that the action was successful - */ - ExitCode[ExitCode["Success"] = 0] = "Success"; - /** - * A code indicating that the action was a failure - */ - ExitCode[ExitCode["Failure"] = 1] = "Failure"; -})(ExitCode = exports.ExitCode || (exports.ExitCode = {})); -//----------------------------------------------------------------------- -// Variables -//----------------------------------------------------------------------- -/** - * Sets env variable for this action and future actions in the job - * @param name the name of the variable to set - * @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function exportVariable(name, val) { - const convertedVal = utils_1.toCommandValue(val); - process.env[name] = convertedVal; - const filePath = process.env['GITHUB_ENV'] || ''; - if (filePath) { - return file_command_1.issueFileCommand('ENV', file_command_1.prepareKeyValueMessage(name, val)); - } - command_1.issueCommand('set-env', { name }, convertedVal); -} -exports.exportVariable = exportVariable; -/** - * Registers a secret which will get masked from logs - * @param secret value of the secret - */ -function setSecret(secret) { - command_1.issueCommand('add-mask', {}, secret); -} -exports.setSecret = setSecret; -/** - * Prepends inputPath to the PATH (for this action and future actions) - * @param inputPath - */ -function addPath(inputPath) { - const filePath = process.env['GITHUB_PATH'] || ''; - if (filePath) { - file_command_1.issueFileCommand('PATH', inputPath); - } - else { - command_1.issueCommand('add-path', {}, inputPath); - } - process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`; -} -exports.addPath = addPath; -/** - * Gets the value of an input. - * Unless trimWhitespace is set to false in InputOptions, the value is also trimmed. - * Returns an empty string if the value is not defined. - * - * @param name name of the input to get - * @param options optional. See InputOptions. - * @returns string - */ -function getInput(name, options) { - const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || ''; - if (options && options.required && !val) { - throw new Error(`Input required and not supplied: ${name}`); - } - if (options && options.trimWhitespace === false) { - return val; - } - return val.trim(); -} -exports.getInput = getInput; -/** - * Gets the values of an multiline input. Each value is also trimmed. - * - * @param name name of the input to get - * @param options optional. See InputOptions. - * @returns string[] - * - */ -function getMultilineInput(name, options) { - const inputs = getInput(name, options) - .split('\n') - .filter(x => x !== ''); - if (options && options.trimWhitespace === false) { - return inputs; - } - return inputs.map(input => input.trim()); -} -exports.getMultilineInput = getMultilineInput; -/** - * Gets the input value of the boolean type in the YAML 1.2 "core schema" specification. - * Support boolean input list: `true | True | TRUE | false | False | FALSE` . - * The return value is also in boolean type. - * ref: https://yaml.org/spec/1.2/spec.html#id2804923 - * - * @param name name of the input to get - * @param options optional. See InputOptions. - * @returns boolean - */ -function getBooleanInput(name, options) { - const trueValue = ['true', 'True', 'TRUE']; - const falseValue = ['false', 'False', 'FALSE']; - const val = getInput(name, options); - if (trueValue.includes(val)) - return true; - if (falseValue.includes(val)) - return false; - throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${name}\n` + - `Support boolean input list: \`true | True | TRUE | false | False | FALSE\``); -} -exports.getBooleanInput = getBooleanInput; -/** - * Sets the value of an output. - * - * @param name name of the output to set - * @param value value to store. Non-string values will be converted to a string via JSON.stringify - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function setOutput(name, value) { - const filePath = process.env['GITHUB_OUTPUT'] || ''; - if (filePath) { - return file_command_1.issueFileCommand('OUTPUT', file_command_1.prepareKeyValueMessage(name, value)); - } - process.stdout.write(os.EOL); - command_1.issueCommand('set-output', { name }, utils_1.toCommandValue(value)); -} -exports.setOutput = setOutput; -/** - * Enables or disables the echoing of commands into stdout for the rest of the step. - * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set. - * - */ -function setCommandEcho(enabled) { - command_1.issue('echo', enabled ? 'on' : 'off'); -} -exports.setCommandEcho = setCommandEcho; -//----------------------------------------------------------------------- -// Results -//----------------------------------------------------------------------- -/** - * Sets the action status to failed. - * When the action exits it will be with an exit code of 1 - * @param message add error issue message - */ -function setFailed(message) { - process.exitCode = ExitCode.Failure; - error(message); -} -exports.setFailed = setFailed; -//----------------------------------------------------------------------- -// Logging Commands -//----------------------------------------------------------------------- -/** - * Gets whether Actions Step Debug is on or not - */ -function isDebug() { - return process.env['RUNNER_DEBUG'] === '1'; -} -exports.isDebug = isDebug; -/** - * Writes debug message to user log - * @param message debug message - */ -function debug(message) { - command_1.issueCommand('debug', {}, message); -} -exports.debug = debug; -/** - * Adds an error issue - * @param message error issue message. Errors will be converted to string via toString() - * @param properties optional properties to add to the annotation. - */ -function error(message, properties = {}) { - command_1.issueCommand('error', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); -} -exports.error = error; -/** - * Adds a warning issue - * @param message warning issue message. Errors will be converted to string via toString() - * @param properties optional properties to add to the annotation. - */ -function warning(message, properties = {}) { - command_1.issueCommand('warning', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); -} -exports.warning = warning; -/** - * Adds a notice issue - * @param message notice issue message. Errors will be converted to string via toString() - * @param properties optional properties to add to the annotation. - */ -function notice(message, properties = {}) { - command_1.issueCommand('notice', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); -} -exports.notice = notice; -/** - * Writes info to log with console.log. - * @param message info message - */ -function info(message) { - process.stdout.write(message + os.EOL); -} -exports.info = info; -/** - * Begin an output group. - * - * Output until the next `groupEnd` will be foldable in this group - * - * @param name The name of the output group - */ -function startGroup(name) { - command_1.issue('group', name); -} -exports.startGroup = startGroup; -/** - * End an output group. - */ -function endGroup() { - command_1.issue('endgroup'); -} -exports.endGroup = endGroup; -/** - * Wrap an asynchronous function call in a group. - * - * Returns the same type as the function itself. - * - * @param name The name of the group - * @param fn The function to wrap in the group - */ -function group(name, fn) { - return __awaiter(this, void 0, void 0, function* () { - startGroup(name); - let result; - try { - result = yield fn(); - } - finally { - endGroup(); - } - return result; - }); -} -exports.group = group; -//----------------------------------------------------------------------- -// Wrapper action state -//----------------------------------------------------------------------- -/** - * Saves state for current action, the state can only be retrieved by this action's post job execution. - * - * @param name name of the state to store - * @param value value to store. Non-string values will be converted to a string via JSON.stringify - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function saveState(name, value) { - const filePath = process.env['GITHUB_STATE'] || ''; - if (filePath) { - return file_command_1.issueFileCommand('STATE', file_command_1.prepareKeyValueMessage(name, value)); - } - command_1.issueCommand('save-state', { name }, utils_1.toCommandValue(value)); -} -exports.saveState = saveState; -/** - * Gets the value of an state set by this action's main execution. - * - * @param name name of the state to get - * @returns string - */ -function getState(name) { - return process.env[`STATE_${name}`] || ''; -} -exports.getState = getState; -function getIDToken(aud) { - return __awaiter(this, void 0, void 0, function* () { - return yield oidc_utils_1.OidcClient.getIDToken(aud); - }); -} -exports.getIDToken = getIDToken; -/** - * Summary exports - */ -var summary_1 = __nccwpck_require__(327); -Object.defineProperty(exports, "summary", ({ enumerable: true, get: function () { return summary_1.summary; } })); -/** - * @deprecated use core.summary - */ -var summary_2 = __nccwpck_require__(327); -Object.defineProperty(exports, "markdownSummary", ({ enumerable: true, get: function () { return summary_2.markdownSummary; } })); -/** - * Path exports - */ -var path_utils_1 = __nccwpck_require__(981); -Object.defineProperty(exports, "toPosixPath", ({ enumerable: true, get: function () { return path_utils_1.toPosixPath; } })); -Object.defineProperty(exports, "toWin32Path", ({ enumerable: true, get: function () { return path_utils_1.toWin32Path; } })); -Object.defineProperty(exports, "toPlatformPath", ({ enumerable: true, get: function () { return path_utils_1.toPlatformPath; } })); -//# sourceMappingURL=core.js.map - -/***/ }), - -/***/ 717: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -// For internal use, subject to change. -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.prepareKeyValueMessage = exports.issueFileCommand = void 0; -// We use any as a valid input type -/* eslint-disable @typescript-eslint/no-explicit-any */ -const fs = __importStar(__nccwpck_require__(147)); -const os = __importStar(__nccwpck_require__(37)); -const uuid_1 = __nccwpck_require__(840); -const utils_1 = __nccwpck_require__(278); -function issueFileCommand(command, message) { - const filePath = process.env[`GITHUB_${command}`]; - if (!filePath) { - throw new Error(`Unable to find environment variable for file command ${command}`); - } - if (!fs.existsSync(filePath)) { - throw new Error(`Missing file at path: ${filePath}`); - } - fs.appendFileSync(filePath, `${utils_1.toCommandValue(message)}${os.EOL}`, { - encoding: 'utf8' - }); -} -exports.issueFileCommand = issueFileCommand; -function prepareKeyValueMessage(key, value) { - const delimiter = `ghadelimiter_${uuid_1.v4()}`; - const convertedValue = utils_1.toCommandValue(value); - // These should realistically never happen, but just in case someone finds a - // way to exploit uuid generation let's not allow keys or values that contain - // the delimiter. - if (key.includes(delimiter)) { - throw new Error(`Unexpected input: name should not contain the delimiter "${delimiter}"`); - } - if (convertedValue.includes(delimiter)) { - throw new Error(`Unexpected input: value should not contain the delimiter "${delimiter}"`); - } - return `${key}<<${delimiter}${os.EOL}${convertedValue}${os.EOL}${delimiter}`; -} -exports.prepareKeyValueMessage = prepareKeyValueMessage; -//# sourceMappingURL=file-command.js.map - -/***/ }), - -/***/ 41: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.OidcClient = void 0; -const http_client_1 = __nccwpck_require__(255); -const auth_1 = __nccwpck_require__(526); -const core_1 = __nccwpck_require__(186); -class OidcClient { - static createHttpClient(allowRetry = true, maxRetry = 10) { - const requestOptions = { - allowRetries: allowRetry, - maxRetries: maxRetry - }; - return new http_client_1.HttpClient('actions/oidc-client', [new auth_1.BearerCredentialHandler(OidcClient.getRequestToken())], requestOptions); - } - static getRequestToken() { - const token = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN']; - if (!token) { - throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable'); - } - return token; - } - static getIDTokenUrl() { - const runtimeUrl = process.env['ACTIONS_ID_TOKEN_REQUEST_URL']; - if (!runtimeUrl) { - throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable'); - } - return runtimeUrl; - } - static getCall(id_token_url) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - const httpclient = OidcClient.createHttpClient(); - const res = yield httpclient - .getJson(id_token_url) - .catch(error => { - throw new Error(`Failed to get ID Token. \n - Error Code : ${error.statusCode}\n - Error Message: ${error.result.message}`); - }); - const id_token = (_a = res.result) === null || _a === void 0 ? void 0 : _a.value; - if (!id_token) { - throw new Error('Response json body do not have ID Token field'); - } - return id_token; - }); - } - static getIDToken(audience) { - return __awaiter(this, void 0, void 0, function* () { - try { - // New ID Token is requested from action service - let id_token_url = OidcClient.getIDTokenUrl(); - if (audience) { - const encodedAudience = encodeURIComponent(audience); - id_token_url = `${id_token_url}&audience=${encodedAudience}`; - } - core_1.debug(`ID token url is ${id_token_url}`); - const id_token = yield OidcClient.getCall(id_token_url); - core_1.setSecret(id_token); - return id_token; - } - catch (error) { - throw new Error(`Error message: ${error.message}`); - } - }); - } -} -exports.OidcClient = OidcClient; -//# sourceMappingURL=oidc-utils.js.map - -/***/ }), - -/***/ 981: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.toPlatformPath = exports.toWin32Path = exports.toPosixPath = void 0; -const path = __importStar(__nccwpck_require__(17)); -/** - * toPosixPath converts the given path to the posix form. On Windows, \\ will be - * replaced with /. - * - * @param pth. Path to transform. - * @return string Posix path. - */ -function toPosixPath(pth) { - return pth.replace(/[\\]/g, '/'); -} -exports.toPosixPath = toPosixPath; -/** - * toWin32Path converts the given path to the win32 form. On Linux, / will be - * replaced with \\. - * - * @param pth. Path to transform. - * @return string Win32 path. - */ -function toWin32Path(pth) { - return pth.replace(/[/]/g, '\\'); -} -exports.toWin32Path = toWin32Path; -/** - * toPlatformPath converts the given path to a platform-specific path. It does - * this by replacing instances of / and \ with the platform-specific path - * separator. - * - * @param pth The path to platformize. - * @return string The platform-specific path. - */ -function toPlatformPath(pth) { - return pth.replace(/[/\\]/g, path.sep); -} -exports.toPlatformPath = toPlatformPath; -//# sourceMappingURL=path-utils.js.map - -/***/ }), - -/***/ 327: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.summary = exports.markdownSummary = exports.SUMMARY_DOCS_URL = exports.SUMMARY_ENV_VAR = void 0; -const os_1 = __nccwpck_require__(37); -const fs_1 = __nccwpck_require__(147); -const { access, appendFile, writeFile } = fs_1.promises; -exports.SUMMARY_ENV_VAR = 'GITHUB_STEP_SUMMARY'; -exports.SUMMARY_DOCS_URL = 'https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary'; -class Summary { - constructor() { - this._buffer = ''; - } - /** - * Finds the summary file path from the environment, rejects if env var is not found or file does not exist - * Also checks r/w permissions. - * - * @returns step summary file path - */ - filePath() { - return __awaiter(this, void 0, void 0, function* () { - if (this._filePath) { - return this._filePath; - } - const pathFromEnv = process.env[exports.SUMMARY_ENV_VAR]; - if (!pathFromEnv) { - throw new Error(`Unable to find environment variable for $${exports.SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.`); - } - try { - yield access(pathFromEnv, fs_1.constants.R_OK | fs_1.constants.W_OK); - } - catch (_a) { - throw new Error(`Unable to access summary file: '${pathFromEnv}'. Check if the file has correct read/write permissions.`); - } - this._filePath = pathFromEnv; - return this._filePath; - }); - } - /** - * Wraps content in an HTML tag, adding any HTML attributes - * - * @param {string} tag HTML tag to wrap - * @param {string | null} content content within the tag - * @param {[attribute: string]: string} attrs key-value list of HTML attributes to add - * - * @returns {string} content wrapped in HTML element - */ - wrap(tag, content, attrs = {}) { - const htmlAttrs = Object.entries(attrs) - .map(([key, value]) => ` ${key}="${value}"`) - .join(''); - if (!content) { - return `<${tag}${htmlAttrs}>`; - } - return `<${tag}${htmlAttrs}>${content}`; - } - /** - * Writes text in the buffer to the summary buffer file and empties buffer. Will append by default. - * - * @param {SummaryWriteOptions} [options] (optional) options for write operation - * - * @returns {Promise} summary instance - */ - write(options) { - return __awaiter(this, void 0, void 0, function* () { - const overwrite = !!(options === null || options === void 0 ? void 0 : options.overwrite); - const filePath = yield this.filePath(); - const writeFunc = overwrite ? writeFile : appendFile; - yield writeFunc(filePath, this._buffer, { encoding: 'utf8' }); - return this.emptyBuffer(); - }); - } - /** - * Clears the summary buffer and wipes the summary file - * - * @returns {Summary} summary instance - */ - clear() { - return __awaiter(this, void 0, void 0, function* () { - return this.emptyBuffer().write({ overwrite: true }); - }); - } - /** - * Returns the current summary buffer as a string - * - * @returns {string} string of summary buffer - */ - stringify() { - return this._buffer; - } - /** - * If the summary buffer is empty - * - * @returns {boolen} true if the buffer is empty - */ - isEmptyBuffer() { - return this._buffer.length === 0; - } - /** - * Resets the summary buffer without writing to summary file - * - * @returns {Summary} summary instance - */ - emptyBuffer() { - this._buffer = ''; - return this; - } - /** - * Adds raw text to the summary buffer - * - * @param {string} text content to add - * @param {boolean} [addEOL=false] (optional) append an EOL to the raw text (default: false) - * - * @returns {Summary} summary instance - */ - addRaw(text, addEOL = false) { - this._buffer += text; - return addEOL ? this.addEOL() : this; - } - /** - * Adds the operating system-specific end-of-line marker to the buffer - * - * @returns {Summary} summary instance - */ - addEOL() { - return this.addRaw(os_1.EOL); - } - /** - * Adds an HTML codeblock to the summary buffer - * - * @param {string} code content to render within fenced code block - * @param {string} lang (optional) language to syntax highlight code - * - * @returns {Summary} summary instance - */ - addCodeBlock(code, lang) { - const attrs = Object.assign({}, (lang && { lang })); - const element = this.wrap('pre', this.wrap('code', code), attrs); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML list to the summary buffer - * - * @param {string[]} items list of items to render - * @param {boolean} [ordered=false] (optional) if the rendered list should be ordered or not (default: false) - * - * @returns {Summary} summary instance - */ - addList(items, ordered = false) { - const tag = ordered ? 'ol' : 'ul'; - const listItems = items.map(item => this.wrap('li', item)).join(''); - const element = this.wrap(tag, listItems); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML table to the summary buffer - * - * @param {SummaryTableCell[]} rows table rows - * - * @returns {Summary} summary instance - */ - addTable(rows) { - const tableBody = rows - .map(row => { - const cells = row - .map(cell => { - if (typeof cell === 'string') { - return this.wrap('td', cell); - } - const { header, data, colspan, rowspan } = cell; - const tag = header ? 'th' : 'td'; - const attrs = Object.assign(Object.assign({}, (colspan && { colspan })), (rowspan && { rowspan })); - return this.wrap(tag, data, attrs); - }) - .join(''); - return this.wrap('tr', cells); - }) - .join(''); - const element = this.wrap('table', tableBody); - return this.addRaw(element).addEOL(); - } - /** - * Adds a collapsable HTML details element to the summary buffer - * - * @param {string} label text for the closed state - * @param {string} content collapsable content - * - * @returns {Summary} summary instance - */ - addDetails(label, content) { - const element = this.wrap('details', this.wrap('summary', label) + content); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML image tag to the summary buffer - * - * @param {string} src path to the image you to embed - * @param {string} alt text description of the image - * @param {SummaryImageOptions} options (optional) addition image attributes - * - * @returns {Summary} summary instance - */ - addImage(src, alt, options) { - const { width, height } = options || {}; - const attrs = Object.assign(Object.assign({}, (width && { width })), (height && { height })); - const element = this.wrap('img', null, Object.assign({ src, alt }, attrs)); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML section heading element - * - * @param {string} text heading text - * @param {number | string} [level=1] (optional) the heading level, default: 1 - * - * @returns {Summary} summary instance - */ - addHeading(text, level) { - const tag = `h${level}`; - const allowedTag = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tag) - ? tag - : 'h1'; - const element = this.wrap(allowedTag, text); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML thematic break (
) to the summary buffer - * - * @returns {Summary} summary instance - */ - addSeparator() { - const element = this.wrap('hr', null); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML line break (
) to the summary buffer - * - * @returns {Summary} summary instance - */ - addBreak() { - const element = this.wrap('br', null); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML blockquote to the summary buffer - * - * @param {string} text quote text - * @param {string} cite (optional) citation url - * - * @returns {Summary} summary instance - */ - addQuote(text, cite) { - const attrs = Object.assign({}, (cite && { cite })); - const element = this.wrap('blockquote', text, attrs); - return this.addRaw(element).addEOL(); - } - /** - * Adds an HTML anchor tag to the summary buffer - * - * @param {string} text link text/content - * @param {string} href hyperlink - * - * @returns {Summary} summary instance - */ - addLink(text, href) { - const element = this.wrap('a', text, { href }); - return this.addRaw(element).addEOL(); - } -} -const _summary = new Summary(); -/** - * @deprecated use `core.summary` - */ -exports.markdownSummary = _summary; -exports.summary = _summary; -//# sourceMappingURL=summary.js.map - -/***/ }), - -/***/ 278: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -// We use any as a valid input type -/* eslint-disable @typescript-eslint/no-explicit-any */ -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.toCommandProperties = exports.toCommandValue = void 0; -/** - * Sanitizes an input into a string so it can be passed into issueCommand safely - * @param input input to sanitize into a string - */ -function toCommandValue(input) { - if (input === null || input === undefined) { - return ''; - } - else if (typeof input === 'string' || input instanceof String) { - return input; - } - return JSON.stringify(input); -} -exports.toCommandValue = toCommandValue; -/** - * - * @param annotationProperties - * @returns The command properties to send with the actual annotation command - * See IssueCommandProperties: https://github.com/actions/runner/blob/main/src/Runner.Worker/ActionCommandManager.cs#L646 - */ -function toCommandProperties(annotationProperties) { - if (!Object.keys(annotationProperties).length) { - return {}; - } - return { - title: annotationProperties.title, - file: annotationProperties.file, - line: annotationProperties.startLine, - endLine: annotationProperties.endLine, - col: annotationProperties.startColumn, - endColumn: annotationProperties.endColumn - }; -} -exports.toCommandProperties = toCommandProperties; -//# sourceMappingURL=utils.js.map - -/***/ }), - -/***/ 526: -/***/ (function(__unused_webpack_module, exports) { - -"use strict"; - -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.PersonalAccessTokenCredentialHandler = exports.BearerCredentialHandler = exports.BasicCredentialHandler = void 0; -class BasicCredentialHandler { - constructor(username, password) { - this.username = username; - this.password = password; - } - prepareRequest(options) { - if (!options.headers) { - throw Error('The request has no headers'); - } - options.headers['Authorization'] = `Basic ${Buffer.from(`${this.username}:${this.password}`).toString('base64')}`; - } - // This handler cannot handle 401 - canHandleAuthentication() { - return false; - } - handleAuthentication() { - return __awaiter(this, void 0, void 0, function* () { - throw new Error('not implemented'); - }); - } -} -exports.BasicCredentialHandler = BasicCredentialHandler; -class BearerCredentialHandler { - constructor(token) { - this.token = token; - } - // currently implements pre-authorization - // TODO: support preAuth = false where it hooks on 401 - prepareRequest(options) { - if (!options.headers) { - throw Error('The request has no headers'); - } - options.headers['Authorization'] = `Bearer ${this.token}`; - } - // This handler cannot handle 401 - canHandleAuthentication() { - return false; - } - handleAuthentication() { - return __awaiter(this, void 0, void 0, function* () { - throw new Error('not implemented'); - }); - } -} -exports.BearerCredentialHandler = BearerCredentialHandler; -class PersonalAccessTokenCredentialHandler { - constructor(token) { - this.token = token; - } - // currently implements pre-authorization - // TODO: support preAuth = false where it hooks on 401 - prepareRequest(options) { - if (!options.headers) { - throw Error('The request has no headers'); - } - options.headers['Authorization'] = `Basic ${Buffer.from(`PAT:${this.token}`).toString('base64')}`; - } - // This handler cannot handle 401 - canHandleAuthentication() { - return false; - } - handleAuthentication() { - return __awaiter(this, void 0, void 0, function* () { - throw new Error('not implemented'); - }); - } -} -exports.PersonalAccessTokenCredentialHandler = PersonalAccessTokenCredentialHandler; -//# sourceMappingURL=auth.js.map - -/***/ }), - -/***/ 255: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -/* eslint-disable @typescript-eslint/no-explicit-any */ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.HttpClient = exports.isHttps = exports.HttpClientResponse = exports.HttpClientError = exports.getProxyUrl = exports.MediaTypes = exports.Headers = exports.HttpCodes = void 0; -const http = __importStar(__nccwpck_require__(685)); -const https = __importStar(__nccwpck_require__(687)); -const pm = __importStar(__nccwpck_require__(835)); -const tunnel = __importStar(__nccwpck_require__(294)); -var HttpCodes; -(function (HttpCodes) { - HttpCodes[HttpCodes["OK"] = 200] = "OK"; - HttpCodes[HttpCodes["MultipleChoices"] = 300] = "MultipleChoices"; - HttpCodes[HttpCodes["MovedPermanently"] = 301] = "MovedPermanently"; - HttpCodes[HttpCodes["ResourceMoved"] = 302] = "ResourceMoved"; - HttpCodes[HttpCodes["SeeOther"] = 303] = "SeeOther"; - HttpCodes[HttpCodes["NotModified"] = 304] = "NotModified"; - HttpCodes[HttpCodes["UseProxy"] = 305] = "UseProxy"; - HttpCodes[HttpCodes["SwitchProxy"] = 306] = "SwitchProxy"; - HttpCodes[HttpCodes["TemporaryRedirect"] = 307] = "TemporaryRedirect"; - HttpCodes[HttpCodes["PermanentRedirect"] = 308] = "PermanentRedirect"; - HttpCodes[HttpCodes["BadRequest"] = 400] = "BadRequest"; - HttpCodes[HttpCodes["Unauthorized"] = 401] = "Unauthorized"; - HttpCodes[HttpCodes["PaymentRequired"] = 402] = "PaymentRequired"; - HttpCodes[HttpCodes["Forbidden"] = 403] = "Forbidden"; - HttpCodes[HttpCodes["NotFound"] = 404] = "NotFound"; - HttpCodes[HttpCodes["MethodNotAllowed"] = 405] = "MethodNotAllowed"; - HttpCodes[HttpCodes["NotAcceptable"] = 406] = "NotAcceptable"; - HttpCodes[HttpCodes["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired"; - HttpCodes[HttpCodes["RequestTimeout"] = 408] = "RequestTimeout"; - HttpCodes[HttpCodes["Conflict"] = 409] = "Conflict"; - HttpCodes[HttpCodes["Gone"] = 410] = "Gone"; - HttpCodes[HttpCodes["TooManyRequests"] = 429] = "TooManyRequests"; - HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError"; - HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented"; - HttpCodes[HttpCodes["BadGateway"] = 502] = "BadGateway"; - HttpCodes[HttpCodes["ServiceUnavailable"] = 503] = "ServiceUnavailable"; - HttpCodes[HttpCodes["GatewayTimeout"] = 504] = "GatewayTimeout"; -})(HttpCodes = exports.HttpCodes || (exports.HttpCodes = {})); -var Headers; -(function (Headers) { - Headers["Accept"] = "accept"; - Headers["ContentType"] = "content-type"; -})(Headers = exports.Headers || (exports.Headers = {})); -var MediaTypes; -(function (MediaTypes) { - MediaTypes["ApplicationJson"] = "application/json"; -})(MediaTypes = exports.MediaTypes || (exports.MediaTypes = {})); -/** - * Returns the proxy URL, depending upon the supplied url and proxy environment variables. - * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com - */ -function getProxyUrl(serverUrl) { - const proxyUrl = pm.getProxyUrl(new URL(serverUrl)); - return proxyUrl ? proxyUrl.href : ''; -} -exports.getProxyUrl = getProxyUrl; -const HttpRedirectCodes = [ - HttpCodes.MovedPermanently, - HttpCodes.ResourceMoved, - HttpCodes.SeeOther, - HttpCodes.TemporaryRedirect, - HttpCodes.PermanentRedirect -]; -const HttpResponseRetryCodes = [ - HttpCodes.BadGateway, - HttpCodes.ServiceUnavailable, - HttpCodes.GatewayTimeout -]; -const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD']; -const ExponentialBackoffCeiling = 10; -const ExponentialBackoffTimeSlice = 5; -class HttpClientError extends Error { - constructor(message, statusCode) { - super(message); - this.name = 'HttpClientError'; - this.statusCode = statusCode; - Object.setPrototypeOf(this, HttpClientError.prototype); - } -} -exports.HttpClientError = HttpClientError; -class HttpClientResponse { - constructor(message) { - this.message = message; - } - readBody() { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () { - let output = Buffer.alloc(0); - this.message.on('data', (chunk) => { - output = Buffer.concat([output, chunk]); - }); - this.message.on('end', () => { - resolve(output.toString()); - }); - })); - }); - } -} -exports.HttpClientResponse = HttpClientResponse; -function isHttps(requestUrl) { - const parsedUrl = new URL(requestUrl); - return parsedUrl.protocol === 'https:'; -} -exports.isHttps = isHttps; -class HttpClient { - constructor(userAgent, handlers, requestOptions) { - this._ignoreSslError = false; - this._allowRedirects = true; - this._allowRedirectDowngrade = false; - this._maxRedirects = 50; - this._allowRetries = false; - this._maxRetries = 1; - this._keepAlive = false; - this._disposed = false; - this.userAgent = userAgent; - this.handlers = handlers || []; - this.requestOptions = requestOptions; - if (requestOptions) { - if (requestOptions.ignoreSslError != null) { - this._ignoreSslError = requestOptions.ignoreSslError; - } - this._socketTimeout = requestOptions.socketTimeout; - if (requestOptions.allowRedirects != null) { - this._allowRedirects = requestOptions.allowRedirects; - } - if (requestOptions.allowRedirectDowngrade != null) { - this._allowRedirectDowngrade = requestOptions.allowRedirectDowngrade; - } - if (requestOptions.maxRedirects != null) { - this._maxRedirects = Math.max(requestOptions.maxRedirects, 0); - } - if (requestOptions.keepAlive != null) { - this._keepAlive = requestOptions.keepAlive; - } - if (requestOptions.allowRetries != null) { - this._allowRetries = requestOptions.allowRetries; - } - if (requestOptions.maxRetries != null) { - this._maxRetries = requestOptions.maxRetries; - } - } - } - options(requestUrl, additionalHeaders) { - return __awaiter(this, void 0, void 0, function* () { - return this.request('OPTIONS', requestUrl, null, additionalHeaders || {}); - }); - } - get(requestUrl, additionalHeaders) { - return __awaiter(this, void 0, void 0, function* () { - return this.request('GET', requestUrl, null, additionalHeaders || {}); - }); - } - del(requestUrl, additionalHeaders) { - return __awaiter(this, void 0, void 0, function* () { - return this.request('DELETE', requestUrl, null, additionalHeaders || {}); - }); - } - post(requestUrl, data, additionalHeaders) { - return __awaiter(this, void 0, void 0, function* () { - return this.request('POST', requestUrl, data, additionalHeaders || {}); - }); - } - patch(requestUrl, data, additionalHeaders) { - return __awaiter(this, void 0, void 0, function* () { - return this.request('PATCH', requestUrl, data, additionalHeaders || {}); - }); - } - put(requestUrl, data, additionalHeaders) { - return __awaiter(this, void 0, void 0, function* () { - return this.request('PUT', requestUrl, data, additionalHeaders || {}); - }); - } - head(requestUrl, additionalHeaders) { - return __awaiter(this, void 0, void 0, function* () { - return this.request('HEAD', requestUrl, null, additionalHeaders || {}); - }); - } - sendStream(verb, requestUrl, stream, additionalHeaders) { - return __awaiter(this, void 0, void 0, function* () { - return this.request(verb, requestUrl, stream, additionalHeaders); - }); - } - /** - * Gets a typed object from an endpoint - * Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise - */ - getJson(requestUrl, additionalHeaders = {}) { - return __awaiter(this, void 0, void 0, function* () { - additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); - const res = yield this.get(requestUrl, additionalHeaders); - return this._processResponse(res, this.requestOptions); - }); - } - postJson(requestUrl, obj, additionalHeaders = {}) { - return __awaiter(this, void 0, void 0, function* () { - const data = JSON.stringify(obj, null, 2); - additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); - additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); - const res = yield this.post(requestUrl, data, additionalHeaders); - return this._processResponse(res, this.requestOptions); - }); - } - putJson(requestUrl, obj, additionalHeaders = {}) { - return __awaiter(this, void 0, void 0, function* () { - const data = JSON.stringify(obj, null, 2); - additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); - additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); - const res = yield this.put(requestUrl, data, additionalHeaders); - return this._processResponse(res, this.requestOptions); - }); - } - patchJson(requestUrl, obj, additionalHeaders = {}) { - return __awaiter(this, void 0, void 0, function* () { - const data = JSON.stringify(obj, null, 2); - additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); - additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); - const res = yield this.patch(requestUrl, data, additionalHeaders); - return this._processResponse(res, this.requestOptions); - }); - } - /** - * Makes a raw http request. - * All other methods such as get, post, patch, and request ultimately call this. - * Prefer get, del, post and patch - */ - request(verb, requestUrl, data, headers) { - return __awaiter(this, void 0, void 0, function* () { - if (this._disposed) { - throw new Error('Client has already been disposed.'); - } - const parsedUrl = new URL(requestUrl); - let info = this._prepareRequest(verb, parsedUrl, headers); - // Only perform retries on reads since writes may not be idempotent. - const maxTries = this._allowRetries && RetryableHttpVerbs.includes(verb) - ? this._maxRetries + 1 - : 1; - let numTries = 0; - let response; - do { - response = yield this.requestRaw(info, data); - // Check if it's an authentication challenge - if (response && - response.message && - response.message.statusCode === HttpCodes.Unauthorized) { - let authenticationHandler; - for (const handler of this.handlers) { - if (handler.canHandleAuthentication(response)) { - authenticationHandler = handler; - break; - } - } - if (authenticationHandler) { - return authenticationHandler.handleAuthentication(this, info, data); - } - else { - // We have received an unauthorized response but have no handlers to handle it. - // Let the response return to the caller. - return response; - } - } - let redirectsRemaining = this._maxRedirects; - while (response.message.statusCode && - HttpRedirectCodes.includes(response.message.statusCode) && - this._allowRedirects && - redirectsRemaining > 0) { - const redirectUrl = response.message.headers['location']; - if (!redirectUrl) { - // if there's no location to redirect to, we won't - break; - } - const parsedRedirectUrl = new URL(redirectUrl); - if (parsedUrl.protocol === 'https:' && - parsedUrl.protocol !== parsedRedirectUrl.protocol && - !this._allowRedirectDowngrade) { - throw new Error('Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.'); - } - // we need to finish reading the response before reassigning response - // which will leak the open socket. - yield response.readBody(); - // strip authorization header if redirected to a different hostname - if (parsedRedirectUrl.hostname !== parsedUrl.hostname) { - for (const header in headers) { - // header names are case insensitive - if (header.toLowerCase() === 'authorization') { - delete headers[header]; - } - } - } - // let's make the request with the new redirectUrl - info = this._prepareRequest(verb, parsedRedirectUrl, headers); - response = yield this.requestRaw(info, data); - redirectsRemaining--; - } - if (!response.message.statusCode || - !HttpResponseRetryCodes.includes(response.message.statusCode)) { - // If not a retry code, return immediately instead of retrying - return response; - } - numTries += 1; - if (numTries < maxTries) { - yield response.readBody(); - yield this._performExponentialBackoff(numTries); - } - } while (numTries < maxTries); - return response; - }); - } - /** - * Needs to be called if keepAlive is set to true in request options. - */ - dispose() { - if (this._agent) { - this._agent.destroy(); - } - this._disposed = true; - } - /** - * Raw request. - * @param info - * @param data - */ - requestRaw(info, data) { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve, reject) => { - function callbackForResult(err, res) { - if (err) { - reject(err); - } - else if (!res) { - // If `err` is not passed, then `res` must be passed. - reject(new Error('Unknown error')); - } - else { - resolve(res); - } - } - this.requestRawWithCallback(info, data, callbackForResult); - }); - }); - } - /** - * Raw request with callback. - * @param info - * @param data - * @param onResult - */ - requestRawWithCallback(info, data, onResult) { - if (typeof data === 'string') { - if (!info.options.headers) { - info.options.headers = {}; - } - info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8'); - } - let callbackCalled = false; - function handleResult(err, res) { - if (!callbackCalled) { - callbackCalled = true; - onResult(err, res); - } - } - const req = info.httpModule.request(info.options, (msg) => { - const res = new HttpClientResponse(msg); - handleResult(undefined, res); - }); - let socket; - req.on('socket', sock => { - socket = sock; - }); - // If we ever get disconnected, we want the socket to timeout eventually - req.setTimeout(this._socketTimeout || 3 * 60000, () => { - if (socket) { - socket.end(); - } - handleResult(new Error(`Request timeout: ${info.options.path}`)); - }); - req.on('error', function (err) { - // err has statusCode property - // res should have headers - handleResult(err); - }); - if (data && typeof data === 'string') { - req.write(data, 'utf8'); - } - if (data && typeof data !== 'string') { - data.on('close', function () { - req.end(); - }); - data.pipe(req); - } - else { - req.end(); - } - } - /** - * Gets an http agent. This function is useful when you need an http agent that handles - * routing through a proxy server - depending upon the url and proxy environment variables. - * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com - */ - getAgent(serverUrl) { - const parsedUrl = new URL(serverUrl); - return this._getAgent(parsedUrl); - } - _prepareRequest(method, requestUrl, headers) { - const info = {}; - info.parsedUrl = requestUrl; - const usingSsl = info.parsedUrl.protocol === 'https:'; - info.httpModule = usingSsl ? https : http; - const defaultPort = usingSsl ? 443 : 80; - info.options = {}; - info.options.host = info.parsedUrl.hostname; - info.options.port = info.parsedUrl.port - ? parseInt(info.parsedUrl.port) - : defaultPort; - info.options.path = - (info.parsedUrl.pathname || '') + (info.parsedUrl.search || ''); - info.options.method = method; - info.options.headers = this._mergeHeaders(headers); - if (this.userAgent != null) { - info.options.headers['user-agent'] = this.userAgent; - } - info.options.agent = this._getAgent(info.parsedUrl); - // gives handlers an opportunity to participate - if (this.handlers) { - for (const handler of this.handlers) { - handler.prepareRequest(info.options); - } - } - return info; - } - _mergeHeaders(headers) { - if (this.requestOptions && this.requestOptions.headers) { - return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers || {})); - } - return lowercaseKeys(headers || {}); - } - _getExistingOrDefaultHeader(additionalHeaders, header, _default) { - let clientHeader; - if (this.requestOptions && this.requestOptions.headers) { - clientHeader = lowercaseKeys(this.requestOptions.headers)[header]; - } - return additionalHeaders[header] || clientHeader || _default; - } - _getAgent(parsedUrl) { - let agent; - const proxyUrl = pm.getProxyUrl(parsedUrl); - const useProxy = proxyUrl && proxyUrl.hostname; - if (this._keepAlive && useProxy) { - agent = this._proxyAgent; - } - if (this._keepAlive && !useProxy) { - agent = this._agent; - } - // if agent is already assigned use that agent. - if (agent) { - return agent; - } - const usingSsl = parsedUrl.protocol === 'https:'; - let maxSockets = 100; - if (this.requestOptions) { - maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets; - } - // This is `useProxy` again, but we need to check `proxyURl` directly for TypeScripts's flow analysis. - if (proxyUrl && proxyUrl.hostname) { - const agentOptions = { - maxSockets, - keepAlive: this._keepAlive, - proxy: Object.assign(Object.assign({}, ((proxyUrl.username || proxyUrl.password) && { - proxyAuth: `${proxyUrl.username}:${proxyUrl.password}` - })), { host: proxyUrl.hostname, port: proxyUrl.port }) - }; - let tunnelAgent; - const overHttps = proxyUrl.protocol === 'https:'; - if (usingSsl) { - tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp; - } - else { - tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp; - } - agent = tunnelAgent(agentOptions); - this._proxyAgent = agent; - } - // if reusing agent across request and tunneling agent isn't assigned create a new agent - if (this._keepAlive && !agent) { - const options = { keepAlive: this._keepAlive, maxSockets }; - agent = usingSsl ? new https.Agent(options) : new http.Agent(options); - this._agent = agent; - } - // if not using private agent and tunnel agent isn't setup then use global agent - if (!agent) { - agent = usingSsl ? https.globalAgent : http.globalAgent; - } - if (usingSsl && this._ignoreSslError) { - // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process - // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options - // we have to cast it to any and change it directly - agent.options = Object.assign(agent.options || {}, { - rejectUnauthorized: false - }); - } - return agent; - } - _performExponentialBackoff(retryNumber) { - return __awaiter(this, void 0, void 0, function* () { - retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber); - const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber); - return new Promise(resolve => setTimeout(() => resolve(), ms)); - }); - } - _processResponse(res, options) { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - const statusCode = res.message.statusCode || 0; - const response = { - statusCode, - result: null, - headers: {} - }; - // not found leads to null obj returned - if (statusCode === HttpCodes.NotFound) { - resolve(response); - } - // get the result from the body - function dateTimeDeserializer(key, value) { - if (typeof value === 'string') { - const a = new Date(value); - if (!isNaN(a.valueOf())) { - return a; - } - } - return value; - } - let obj; - let contents; - try { - contents = yield res.readBody(); - if (contents && contents.length > 0) { - if (options && options.deserializeDates) { - obj = JSON.parse(contents, dateTimeDeserializer); - } - else { - obj = JSON.parse(contents); - } - response.result = obj; - } - response.headers = res.message.headers; - } - catch (err) { - // Invalid resource (contents not json); leaving result obj null - } - // note that 3xx redirects are handled by the http layer. - if (statusCode > 299) { - let msg; - // if exception/error in body, attempt to get better error - if (obj && obj.message) { - msg = obj.message; - } - else if (contents && contents.length > 0) { - // it may be the case that the exception is in the body message as string - msg = contents; - } - else { - msg = `Failed request: (${statusCode})`; - } - const err = new HttpClientError(msg, statusCode); - err.result = response.result; - reject(err); - } - else { - resolve(response); - } - })); - }); - } -} -exports.HttpClient = HttpClient; -const lowercaseKeys = (obj) => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {}); -//# sourceMappingURL=index.js.map - -/***/ }), - -/***/ 835: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.checkBypass = exports.getProxyUrl = void 0; -function getProxyUrl(reqUrl) { - const usingSsl = reqUrl.protocol === 'https:'; - if (checkBypass(reqUrl)) { - return undefined; - } - const proxyVar = (() => { - if (usingSsl) { - return process.env['https_proxy'] || process.env['HTTPS_PROXY']; - } - else { - return process.env['http_proxy'] || process.env['HTTP_PROXY']; - } - })(); - if (proxyVar) { - return new URL(proxyVar); - } - else { - return undefined; - } -} -exports.getProxyUrl = getProxyUrl; -function checkBypass(reqUrl) { - if (!reqUrl.hostname) { - return false; - } - const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''; - if (!noProxy) { - return false; - } - // Determine the request port - let reqPort; - if (reqUrl.port) { - reqPort = Number(reqUrl.port); - } - else if (reqUrl.protocol === 'http:') { - reqPort = 80; - } - else if (reqUrl.protocol === 'https:') { - reqPort = 443; - } - // Format the request hostname and hostname with port - const upperReqHosts = [reqUrl.hostname.toUpperCase()]; - if (typeof reqPort === 'number') { - upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`); - } - // Compare request host against noproxy - for (const upperNoProxyItem of noProxy - .split(',') - .map(x => x.trim().toUpperCase()) - .filter(x => x)) { - if (upperReqHosts.some(x => x === upperNoProxyItem)) { - return true; - } - } - return false; -} -exports.checkBypass = checkBypass; -//# sourceMappingURL=proxy.js.map - -/***/ }), - -/***/ 294: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - -module.exports = __nccwpck_require__(219); - - -/***/ }), - -/***/ 219: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -var net = __nccwpck_require__(808); -var tls = __nccwpck_require__(404); -var http = __nccwpck_require__(685); -var https = __nccwpck_require__(687); -var events = __nccwpck_require__(361); -var assert = __nccwpck_require__(491); -var util = __nccwpck_require__(837); - - -exports.httpOverHttp = httpOverHttp; -exports.httpsOverHttp = httpsOverHttp; -exports.httpOverHttps = httpOverHttps; -exports.httpsOverHttps = httpsOverHttps; - - -function httpOverHttp(options) { - var agent = new TunnelingAgent(options); - agent.request = http.request; - return agent; -} - -function httpsOverHttp(options) { - var agent = new TunnelingAgent(options); - agent.request = http.request; - agent.createSocket = createSecureSocket; - agent.defaultPort = 443; - return agent; -} - -function httpOverHttps(options) { - var agent = new TunnelingAgent(options); - agent.request = https.request; - return agent; -} - -function httpsOverHttps(options) { - var agent = new TunnelingAgent(options); - agent.request = https.request; - agent.createSocket = createSecureSocket; - agent.defaultPort = 443; - return agent; -} - - -function TunnelingAgent(options) { - var self = this; - self.options = options || {}; - self.proxyOptions = self.options.proxy || {}; - self.maxSockets = self.options.maxSockets || http.Agent.defaultMaxSockets; - self.requests = []; - self.sockets = []; - - self.on('free', function onFree(socket, host, port, localAddress) { - var options = toOptions(host, port, localAddress); - for (var i = 0, len = self.requests.length; i < len; ++i) { - var pending = self.requests[i]; - if (pending.host === options.host && pending.port === options.port) { - // Detect the request to connect same origin server, - // reuse the connection. - self.requests.splice(i, 1); - pending.request.onSocket(socket); - return; - } - } - socket.destroy(); - self.removeSocket(socket); - }); -} -util.inherits(TunnelingAgent, events.EventEmitter); - -TunnelingAgent.prototype.addRequest = function addRequest(req, host, port, localAddress) { - var self = this; - var options = mergeOptions({request: req}, self.options, toOptions(host, port, localAddress)); - - if (self.sockets.length >= this.maxSockets) { - // We are over limit so we'll add it to the queue. - self.requests.push(options); - return; - } - - // If we are under maxSockets create a new one. - self.createSocket(options, function(socket) { - socket.on('free', onFree); - socket.on('close', onCloseOrRemove); - socket.on('agentRemove', onCloseOrRemove); - req.onSocket(socket); - - function onFree() { - self.emit('free', socket, options); - } - - function onCloseOrRemove(err) { - self.removeSocket(socket); - socket.removeListener('free', onFree); - socket.removeListener('close', onCloseOrRemove); - socket.removeListener('agentRemove', onCloseOrRemove); - } - }); -}; - -TunnelingAgent.prototype.createSocket = function createSocket(options, cb) { - var self = this; - var placeholder = {}; - self.sockets.push(placeholder); - - var connectOptions = mergeOptions({}, self.proxyOptions, { - method: 'CONNECT', - path: options.host + ':' + options.port, - agent: false, - headers: { - host: options.host + ':' + options.port - } - }); - if (options.localAddress) { - connectOptions.localAddress = options.localAddress; - } - if (connectOptions.proxyAuth) { - connectOptions.headers = connectOptions.headers || {}; - connectOptions.headers['Proxy-Authorization'] = 'Basic ' + - new Buffer(connectOptions.proxyAuth).toString('base64'); - } - - debug('making CONNECT request'); - var connectReq = self.request(connectOptions); - connectReq.useChunkedEncodingByDefault = false; // for v0.6 - connectReq.once('response', onResponse); // for v0.6 - connectReq.once('upgrade', onUpgrade); // for v0.6 - connectReq.once('connect', onConnect); // for v0.7 or later - connectReq.once('error', onError); - connectReq.end(); - - function onResponse(res) { - // Very hacky. This is necessary to avoid http-parser leaks. - res.upgrade = true; - } - - function onUpgrade(res, socket, head) { - // Hacky. - process.nextTick(function() { - onConnect(res, socket, head); - }); - } - - function onConnect(res, socket, head) { - connectReq.removeAllListeners(); - socket.removeAllListeners(); - - if (res.statusCode !== 200) { - debug('tunneling socket could not be established, statusCode=%d', - res.statusCode); - socket.destroy(); - var error = new Error('tunneling socket could not be established, ' + - 'statusCode=' + res.statusCode); - error.code = 'ECONNRESET'; - options.request.emit('error', error); - self.removeSocket(placeholder); - return; - } - if (head.length > 0) { - debug('got illegal response body from proxy'); - socket.destroy(); - var error = new Error('got illegal response body from proxy'); - error.code = 'ECONNRESET'; - options.request.emit('error', error); - self.removeSocket(placeholder); - return; - } - debug('tunneling connection has established'); - self.sockets[self.sockets.indexOf(placeholder)] = socket; - return cb(socket); - } - - function onError(cause) { - connectReq.removeAllListeners(); - - debug('tunneling socket could not be established, cause=%s\n', - cause.message, cause.stack); - var error = new Error('tunneling socket could not be established, ' + - 'cause=' + cause.message); - error.code = 'ECONNRESET'; - options.request.emit('error', error); - self.removeSocket(placeholder); - } -}; - -TunnelingAgent.prototype.removeSocket = function removeSocket(socket) { - var pos = this.sockets.indexOf(socket) - if (pos === -1) { - return; - } - this.sockets.splice(pos, 1); - - var pending = this.requests.shift(); - if (pending) { - // If we have pending requests and a socket gets closed a new one - // needs to be created to take over in the pool for the one that closed. - this.createSocket(pending, function(socket) { - pending.request.onSocket(socket); - }); - } -}; - -function createSecureSocket(options, cb) { - var self = this; - TunnelingAgent.prototype.createSocket.call(self, options, function(socket) { - var hostHeader = options.request.getHeader('host'); - var tlsOptions = mergeOptions({}, self.options, { - socket: socket, - servername: hostHeader ? hostHeader.replace(/:.*$/, '') : options.host - }); - - // 0 is dummy port for v0.6 - var secureSocket = tls.connect(0, tlsOptions); - self.sockets[self.sockets.indexOf(socket)] = secureSocket; - cb(secureSocket); - }); -} - - -function toOptions(host, port, localAddress) { - if (typeof host === 'string') { // since v0.10 - return { - host: host, - port: port, - localAddress: localAddress - }; - } - return host; // for v0.11 or later -} - -function mergeOptions(target) { - for (var i = 1, len = arguments.length; i < len; ++i) { - var overrides = arguments[i]; - if (typeof overrides === 'object') { - var keys = Object.keys(overrides); - for (var j = 0, keyLen = keys.length; j < keyLen; ++j) { - var k = keys[j]; - if (overrides[k] !== undefined) { - target[k] = overrides[k]; - } - } - } - } - return target; -} - - -var debug; -if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) { - debug = function() { - var args = Array.prototype.slice.call(arguments); - if (typeof args[0] === 'string') { - args[0] = 'TUNNEL: ' + args[0]; - } else { - args.unshift('TUNNEL:'); - } - console.error.apply(console, args); - } -} else { - debug = function() {}; -} -exports.debug = debug; // for test - - -/***/ }), - -/***/ 840: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -Object.defineProperty(exports, "v1", ({ - enumerable: true, - get: function () { - return _v.default; - } -})); -Object.defineProperty(exports, "v3", ({ - enumerable: true, - get: function () { - return _v2.default; - } -})); -Object.defineProperty(exports, "v4", ({ - enumerable: true, - get: function () { - return _v3.default; - } -})); -Object.defineProperty(exports, "v5", ({ - enumerable: true, - get: function () { - return _v4.default; - } -})); -Object.defineProperty(exports, "NIL", ({ - enumerable: true, - get: function () { - return _nil.default; - } -})); -Object.defineProperty(exports, "version", ({ - enumerable: true, - get: function () { - return _version.default; - } -})); -Object.defineProperty(exports, "validate", ({ - enumerable: true, - get: function () { - return _validate.default; - } -})); -Object.defineProperty(exports, "stringify", ({ - enumerable: true, - get: function () { - return _stringify.default; - } -})); -Object.defineProperty(exports, "parse", ({ - enumerable: true, - get: function () { - return _parse.default; - } -})); - -var _v = _interopRequireDefault(__nccwpck_require__(628)); - -var _v2 = _interopRequireDefault(__nccwpck_require__(409)); - -var _v3 = _interopRequireDefault(__nccwpck_require__(122)); - -var _v4 = _interopRequireDefault(__nccwpck_require__(120)); - -var _nil = _interopRequireDefault(__nccwpck_require__(332)); - -var _version = _interopRequireDefault(__nccwpck_require__(595)); - -var _validate = _interopRequireDefault(__nccwpck_require__(900)); - -var _stringify = _interopRequireDefault(__nccwpck_require__(950)); - -var _parse = _interopRequireDefault(__nccwpck_require__(746)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -/***/ }), - -/***/ 569: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _crypto = _interopRequireDefault(__nccwpck_require__(113)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function md5(bytes) { - if (Array.isArray(bytes)) { - bytes = Buffer.from(bytes); - } else if (typeof bytes === 'string') { - bytes = Buffer.from(bytes, 'utf8'); - } - - return _crypto.default.createHash('md5').update(bytes).digest(); -} - -var _default = md5; -exports["default"] = _default; - -/***/ }), - -/***/ 332: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; -var _default = '00000000-0000-0000-0000-000000000000'; -exports["default"] = _default; - -/***/ }), - -/***/ 746: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _validate = _interopRequireDefault(__nccwpck_require__(900)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function parse(uuid) { - if (!(0, _validate.default)(uuid)) { - throw TypeError('Invalid UUID'); - } - - let v; - const arr = new Uint8Array(16); // Parse ########-....-....-....-............ - - arr[0] = (v = parseInt(uuid.slice(0, 8), 16)) >>> 24; - arr[1] = v >>> 16 & 0xff; - arr[2] = v >>> 8 & 0xff; - arr[3] = v & 0xff; // Parse ........-####-....-....-............ - - arr[4] = (v = parseInt(uuid.slice(9, 13), 16)) >>> 8; - arr[5] = v & 0xff; // Parse ........-....-####-....-............ - - arr[6] = (v = parseInt(uuid.slice(14, 18), 16)) >>> 8; - arr[7] = v & 0xff; // Parse ........-....-....-####-............ - - arr[8] = (v = parseInt(uuid.slice(19, 23), 16)) >>> 8; - arr[9] = v & 0xff; // Parse ........-....-....-....-############ - // (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes) - - arr[10] = (v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000 & 0xff; - arr[11] = v / 0x100000000 & 0xff; - arr[12] = v >>> 24 & 0xff; - arr[13] = v >>> 16 & 0xff; - arr[14] = v >>> 8 & 0xff; - arr[15] = v & 0xff; - return arr; -} - -var _default = parse; -exports["default"] = _default; - -/***/ }), - -/***/ 814: -/***/ ((__unused_webpack_module, exports) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; -var _default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i; -exports["default"] = _default; - -/***/ }), - -/***/ 807: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = rng; - -var _crypto = _interopRequireDefault(__nccwpck_require__(113)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const rnds8Pool = new Uint8Array(256); // # of random values to pre-allocate - -let poolPtr = rnds8Pool.length; - -function rng() { - if (poolPtr > rnds8Pool.length - 16) { - _crypto.default.randomFillSync(rnds8Pool); - - poolPtr = 0; - } - - return rnds8Pool.slice(poolPtr, poolPtr += 16); -} - -/***/ }), - -/***/ 274: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _crypto = _interopRequireDefault(__nccwpck_require__(113)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function sha1(bytes) { - if (Array.isArray(bytes)) { - bytes = Buffer.from(bytes); - } else if (typeof bytes === 'string') { - bytes = Buffer.from(bytes, 'utf8'); - } - - return _crypto.default.createHash('sha1').update(bytes).digest(); -} - -var _default = sha1; -exports["default"] = _default; - -/***/ }), - -/***/ 950: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _validate = _interopRequireDefault(__nccwpck_require__(900)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -/** - * Convert array of 16 byte values to UUID string format of the form: - * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX - */ -const byteToHex = []; - -for (let i = 0; i < 256; ++i) { - byteToHex.push((i + 0x100).toString(16).substr(1)); -} - -function stringify(arr, offset = 0) { - // Note: Be careful editing this code! It's been tuned for performance - // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434 - const uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); // Consistency check for valid UUID. If this throws, it's likely due to one - // of the following: - // - One or more input array values don't map to a hex octet (leading to - // "undefined" in the uuid) - // - Invalid input values for the RFC `version` or `variant` fields - - if (!(0, _validate.default)(uuid)) { - throw TypeError('Stringified UUID is invalid'); - } - - return uuid; -} - -var _default = stringify; -exports["default"] = _default; - -/***/ }), - -/***/ 628: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _rng = _interopRequireDefault(__nccwpck_require__(807)); - -var _stringify = _interopRequireDefault(__nccwpck_require__(950)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -// **`v1()` - Generate time-based UUID** -// -// Inspired by https://github.com/LiosK/UUID.js -// and http://docs.python.org/library/uuid.html -let _nodeId; - -let _clockseq; // Previous uuid creation time - - -let _lastMSecs = 0; -let _lastNSecs = 0; // See https://github.com/uuidjs/uuid for API details - -function v1(options, buf, offset) { - let i = buf && offset || 0; - const b = buf || new Array(16); - options = options || {}; - let node = options.node || _nodeId; - let clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; // node and clockseq need to be initialized to random values if they're not - // specified. We do this lazily to minimize issues related to insufficient - // system entropy. See #189 - - if (node == null || clockseq == null) { - const seedBytes = options.random || (options.rng || _rng.default)(); - - if (node == null) { - // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1) - node = _nodeId = [seedBytes[0] | 0x01, seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5]]; - } - - if (clockseq == null) { - // Per 4.2.2, randomize (14 bit) clockseq - clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff; - } - } // UUID timestamps are 100 nano-second units since the Gregorian epoch, - // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so - // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs' - // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00. - - - let msecs = options.msecs !== undefined ? options.msecs : Date.now(); // Per 4.2.1.2, use count of uuid's generated during the current clock - // cycle to simulate higher resolution clock - - let nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; // Time since last uuid creation (in msecs) - - const dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000; // Per 4.2.1.2, Bump clockseq on clock regression - - if (dt < 0 && options.clockseq === undefined) { - clockseq = clockseq + 1 & 0x3fff; - } // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new - // time interval - - - if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) { - nsecs = 0; - } // Per 4.2.1.2 Throw error if too many uuids are requested - - - if (nsecs >= 10000) { - throw new Error("uuid.v1(): Can't create more than 10M uuids/sec"); - } - - _lastMSecs = msecs; - _lastNSecs = nsecs; - _clockseq = clockseq; // Per 4.1.4 - Convert from unix epoch to Gregorian epoch - - msecs += 12219292800000; // `time_low` - - const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; - b[i++] = tl >>> 24 & 0xff; - b[i++] = tl >>> 16 & 0xff; - b[i++] = tl >>> 8 & 0xff; - b[i++] = tl & 0xff; // `time_mid` - - const tmh = msecs / 0x100000000 * 10000 & 0xfffffff; - b[i++] = tmh >>> 8 & 0xff; - b[i++] = tmh & 0xff; // `time_high_and_version` - - b[i++] = tmh >>> 24 & 0xf | 0x10; // include version - - b[i++] = tmh >>> 16 & 0xff; // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) - - b[i++] = clockseq >>> 8 | 0x80; // `clock_seq_low` - - b[i++] = clockseq & 0xff; // `node` - - for (let n = 0; n < 6; ++n) { - b[i + n] = node[n]; - } - - return buf || (0, _stringify.default)(b); -} - -var _default = v1; -exports["default"] = _default; - -/***/ }), - -/***/ 409: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _v = _interopRequireDefault(__nccwpck_require__(998)); - -var _md = _interopRequireDefault(__nccwpck_require__(569)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const v3 = (0, _v.default)('v3', 0x30, _md.default); -var _default = v3; -exports["default"] = _default; - -/***/ }), - -/***/ 998: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = _default; -exports.URL = exports.DNS = void 0; - -var _stringify = _interopRequireDefault(__nccwpck_require__(950)); - -var _parse = _interopRequireDefault(__nccwpck_require__(746)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function stringToBytes(str) { - str = unescape(encodeURIComponent(str)); // UTF8 escape - - const bytes = []; - - for (let i = 0; i < str.length; ++i) { - bytes.push(str.charCodeAt(i)); - } - - return bytes; -} - -const DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; -exports.DNS = DNS; -const URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8'; -exports.URL = URL; - -function _default(name, version, hashfunc) { - function generateUUID(value, namespace, buf, offset) { - if (typeof value === 'string') { - value = stringToBytes(value); - } - - if (typeof namespace === 'string') { - namespace = (0, _parse.default)(namespace); - } - - if (namespace.length !== 16) { - throw TypeError('Namespace must be array-like (16 iterable integer values, 0-255)'); - } // Compute hash of namespace and value, Per 4.3 - // Future: Use spread syntax when supported on all platforms, e.g. `bytes = - // hashfunc([...namespace, ... value])` - - - let bytes = new Uint8Array(16 + value.length); - bytes.set(namespace); - bytes.set(value, namespace.length); - bytes = hashfunc(bytes); - bytes[6] = bytes[6] & 0x0f | version; - bytes[8] = bytes[8] & 0x3f | 0x80; - - if (buf) { - offset = offset || 0; - - for (let i = 0; i < 16; ++i) { - buf[offset + i] = bytes[i]; - } - - return buf; - } - - return (0, _stringify.default)(bytes); - } // Function#name is not settable on some platforms (#270) - - - try { - generateUUID.name = name; // eslint-disable-next-line no-empty - } catch (err) {} // For CommonJS default export support - - - generateUUID.DNS = DNS; - generateUUID.URL = URL; - return generateUUID; -} - -/***/ }), - -/***/ 122: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _rng = _interopRequireDefault(__nccwpck_require__(807)); - -var _stringify = _interopRequireDefault(__nccwpck_require__(950)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function v4(options, buf, offset) { - options = options || {}; - - const rnds = options.random || (options.rng || _rng.default)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` - - - rnds[6] = rnds[6] & 0x0f | 0x40; - rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided - - if (buf) { - offset = offset || 0; - - for (let i = 0; i < 16; ++i) { - buf[offset + i] = rnds[i]; - } - - return buf; - } - - return (0, _stringify.default)(rnds); -} - -var _default = v4; -exports["default"] = _default; - -/***/ }), - -/***/ 120: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _v = _interopRequireDefault(__nccwpck_require__(998)); - -var _sha = _interopRequireDefault(__nccwpck_require__(274)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -const v5 = (0, _v.default)('v5', 0x50, _sha.default); -var _default = v5; -exports["default"] = _default; - -/***/ }), - -/***/ 900: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _regex = _interopRequireDefault(__nccwpck_require__(814)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function validate(uuid) { - return typeof uuid === 'string' && _regex.default.test(uuid); -} - -var _default = validate; -exports["default"] = _default; - -/***/ }), - -/***/ 595: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { - -"use strict"; - - -Object.defineProperty(exports, "__esModule", ({ - value: true -})); -exports["default"] = void 0; - -var _validate = _interopRequireDefault(__nccwpck_require__(900)); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function version(uuid) { - if (!(0, _validate.default)(uuid)) { - throw TypeError('Invalid UUID'); - } - - return parseInt(uuid.substr(14, 1), 16); -} - -var _default = version; -exports["default"] = _default; - -/***/ }), - -/***/ 83: -/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { - -"use strict"; - -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", ({ value: true })); -/* eslint-disable @typescript-eslint/naming-convention */ -const core = __importStar(__nccwpck_require__(186)); -const run = function () { - const oldList = JSON.parse(core.getInput('OLD_LIST', { required: true })); - const newList = JSON.parse(core.getInput('NEW_LIST', { required: true })); - const errors = []; - oldList.success.forEach((file) => { - if (newList.success.includes(file) || !newList.failure.includes(file)) { - return; - } - errors.push(file); - }); - if (errors.length > 0) { - errors.forEach((error) => console.error(error)); - throw new Error('Some files could be compiled with react-compiler before successfully, but now they can not be compiled. Check https://github.com/Expensify/App/blob/main/contributingGuides/REACT_COMPILER.md documentation to see how you can fix this.'); - } - return Promise.resolve(); -}; -if (require.main === require.cache[eval('__filename')]) { - run(); -} -exports["default"] = run; - - -/***/ }), - -/***/ 491: -/***/ ((module) => { - -"use strict"; -module.exports = require("assert"); - -/***/ }), - -/***/ 113: -/***/ ((module) => { - -"use strict"; -module.exports = require("crypto"); - -/***/ }), - -/***/ 361: -/***/ ((module) => { - -"use strict"; -module.exports = require("events"); - -/***/ }), - -/***/ 147: -/***/ ((module) => { - -"use strict"; -module.exports = require("fs"); - -/***/ }), - -/***/ 685: -/***/ ((module) => { - -"use strict"; -module.exports = require("http"); - -/***/ }), - -/***/ 687: -/***/ ((module) => { - -"use strict"; -module.exports = require("https"); - -/***/ }), - -/***/ 808: -/***/ ((module) => { - -"use strict"; -module.exports = require("net"); - -/***/ }), - -/***/ 37: -/***/ ((module) => { - -"use strict"; -module.exports = require("os"); - -/***/ }), - -/***/ 17: -/***/ ((module) => { - -"use strict"; -module.exports = require("path"); - -/***/ }), - -/***/ 404: -/***/ ((module) => { - -"use strict"; -module.exports = require("tls"); - -/***/ }), - -/***/ 837: -/***/ ((module) => { - -"use strict"; -module.exports = require("util"); - -/***/ }) - -/******/ }); -/************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __nccwpck_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ var threw = true; -/******/ try { -/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __nccwpck_require__); -/******/ threw = false; -/******/ } finally { -/******/ if(threw) delete __webpack_module_cache__[moduleId]; -/******/ } -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/************************************************************************/ -/******/ /* webpack/runtime/compat */ -/******/ -/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/"; -/******/ -/************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module is referenced by other modules so it can't be inlined -/******/ var __webpack_exports__ = __nccwpck_require__(83); -/******/ module.exports = __webpack_exports__; -/******/ -/******/ })() -; diff --git a/.github/actions/javascript/getDeployPullRequestList/getDeployPullRequestList.ts b/.github/actions/javascript/getDeployPullRequestList/getDeployPullRequestList.ts index 5d5dbc7e2f29..2c854b37eb20 100644 --- a/.github/actions/javascript/getDeployPullRequestList/getDeployPullRequestList.ts +++ b/.github/actions/javascript/getDeployPullRequestList/getDeployPullRequestList.ts @@ -1,79 +1,9 @@ import * as core from '@actions/core'; import * as github from '@actions/github'; -import type {RestEndpointMethodTypes} from '@octokit/plugin-rest-endpoint-methods/dist-types/generated/parameters-and-response-types'; import {getJSONInput} from '@github/libs/ActionUtils'; import GithubUtils from '@github/libs/GithubUtils'; import GitUtils from '@github/libs/GitUtils'; -type WorkflowRun = RestEndpointMethodTypes['actions']['listWorkflowRuns']['response']['data']['workflow_runs'][number]; - -const BUILD_AND_DEPLOY_JOB_NAME_PREFIX = 'Build and deploy'; - -/** - * This function checks if a given release is a valid baseTag to get the PR list with `git log baseTag...endTag`. - * - * The rules are: - * - production deploys can only be compared with other production deploys - * - staging deploys can be compared with other staging deploys or production deploys. - * The reason is that the final staging release in each deploy cycle will BECOME a production release. - * For example, imagine a checklist is closed with version 9.0.20-6; that's the most recent staging deploy, but the release for 9.0.20-6 is now finalized, so it looks like a prod deploy. - * When 9.0.21-0 finishes deploying to staging, the most recent prerelease is 9.0.20-5. However, we want 9.0.20-6...9.0.21-0, - * NOT 9.0.20-5...9.0.21-0 (so that the PR CP'd in 9.0.20-6 is not included in the next checklist) - */ -async function isReleaseValidBaseForEnvironment(releaseTag: string, isProductionDeploy: boolean) { - if (!isProductionDeploy) { - return true; - } - const isPrerelease = ( - await GithubUtils.octokit.repos.getReleaseByTag({ - owner: github.context.repo.owner, - repo: github.context.repo.repo, - tag: releaseTag, - }) - ).data.prerelease; - return !isPrerelease; -} - -/** - * Was a given deploy workflow run successful on at least one platform? - */ -async function wasDeploySuccessful(runID: number) { - const jobsForWorkflowRun = ( - await GithubUtils.octokit.actions.listJobsForWorkflowRun({ - owner: github.context.repo.owner, - repo: github.context.repo.repo, - // eslint-disable-next-line @typescript-eslint/naming-convention - run_id: runID, - filter: 'latest', - }) - ).data.jobs; - return jobsForWorkflowRun.some((job) => job.name.startsWith(BUILD_AND_DEPLOY_JOB_NAME_PREFIX) && job.conclusion === 'success'); -} - -/** - * This function checks if a given deploy workflow is a valid basis for comparison when listing PRs merged between two versions. - * It returns the reason a version should be skipped, or an empty string if the version should not be skipped. - */ -async function shouldSkipVersion(lastSuccessfulDeploy: WorkflowRun, inputTag: string, isProductionDeploy: boolean): Promise { - if (!lastSuccessfulDeploy?.head_branch) { - // This should never happen. Just doing this to appease TS. - return ''; - } - - // we never want to compare a tag with itself. This check is necessary because prod deploys almost always have the same version as the last staging deploy. - // In this case, the next for wrong environment fails because the release that triggered that staging deploy is now finalized, so it looks like a prod deploy. - if (lastSuccessfulDeploy?.head_branch === inputTag) { - return `Same as input tag ${inputTag}`; - } - if (!(await isReleaseValidBaseForEnvironment(lastSuccessfulDeploy?.head_branch, isProductionDeploy))) { - return 'Was a staging deploy, we only want to compare with other production deploys'; - } - if (!(await wasDeploySuccessful(lastSuccessfulDeploy.id))) { - return 'Was an unsuccessful deploy, nothing was deployed in that version'; - } - return ''; -} - async function run() { try { const inputTag = core.getInput('TAG', {required: true}); @@ -82,62 +12,56 @@ async function run() { console.log(`Looking for PRs deployed to ${deployEnv} in ${inputTag}...`); - const platformDeploys = ( - await GithubUtils.octokit.actions.listWorkflowRuns({ - owner: github.context.repo.owner, - repo: github.context.repo.repo, - // eslint-disable-next-line @typescript-eslint/naming-convention - workflow_id: 'platformDeploy.yml', - status: 'completed', - }) - ).data.workflow_runs - // Note: we filter out cancelled runs instead of looking only for success runs - // because if a build fails on even one platform, then it will have the status 'failure' - .filter((workflowRun) => workflowRun.conclusion !== 'cancelled'); - - const deploys = ( - await GithubUtils.octokit.actions.listWorkflowRuns({ + let priorTag: string | undefined; + let foundCurrentRelease = false; + await GithubUtils.paginate( + GithubUtils.octokit.repos.listReleases, + { owner: github.context.repo.owner, repo: github.context.repo.repo, // eslint-disable-next-line @typescript-eslint/naming-convention - workflow_id: 'deploy.yml', - status: 'completed', - }) - ).data.workflow_runs - // Note: we filter out cancelled runs instead of looking only for success runs - // because if a build fails on even one platform, then it will have the status 'failure' - .filter((workflowRun) => workflowRun.conclusion !== 'cancelled'); - - // W've combined platformDeploy.yml and deploy.yml - // TODO: Remove this once there are successful staging and production deploys using the new deploy.yml workflow - const completedDeploys = [...deploys, ...platformDeploys]; - completedDeploys.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); - - // Find the most recent deploy workflow targeting the correct environment, for which at least one of the build jobs finished successfully - let lastSuccessfulDeploy = completedDeploys.shift(); - - if (!lastSuccessfulDeploy) { - throw new Error('Could not find a prior successful deploy'); - } - - let reason = await shouldSkipVersion(lastSuccessfulDeploy, inputTag, isProductionDeploy); - while (lastSuccessfulDeploy && reason) { - console.log( - `Deploy of tag ${lastSuccessfulDeploy.head_branch} was not valid as a base for comparison, looking at the next one. Reason: ${reason}`, - lastSuccessfulDeploy.html_url, - ); - lastSuccessfulDeploy = completedDeploys.shift(); - - if (!lastSuccessfulDeploy) { - throw new Error('Could not find a prior successful deploy'); - } - - reason = await shouldSkipVersion(lastSuccessfulDeploy, inputTag, isProductionDeploy); + per_page: 100, + }, + ({data}, done) => { + // For production deploys, look only at other production deploys. + // staging deploys can be compared with other staging deploys or production deploys. + // The reason is that the final staging release in each deploy cycle will BECOME a production release + const filteredData = isProductionDeploy ? data.filter((release) => !release.prerelease) : data; + + // Release was in the last page, meaning the previous release is the first item in this page + if (foundCurrentRelease) { + priorTag = data.at(0)?.tag_name; + done(); + return filteredData; + } + + // Search for the index of input tag + const indexOfCurrentRelease = filteredData.findIndex((release) => release.tag_name === inputTag); + + // If it happens to be at the end of this page, then the previous tag will be in the next page. + // Set a flag showing we found it so we grab the first release of the next page + if (indexOfCurrentRelease === filteredData.length - 1) { + foundCurrentRelease = true; + return filteredData; + } + + // If it's anywhere else in this page, the the prior release is the next item in the page + if (indexOfCurrentRelease >= 0) { + priorTag = filteredData.at(indexOfCurrentRelease + 1)?.tag_name; + done(); + } + + // Release not in this page (or we're done) + return filteredData; + }, + ); + + if (!priorTag) { + throw new Error('Something went wrong and the prior tag could not be found.'); } - const priorTag = lastSuccessfulDeploy.head_branch; console.log(`Looking for PRs deployed to ${deployEnv} between ${priorTag} and ${inputTag}`); - const prList = await GitUtils.getPullRequestsMergedBetween(priorTag ?? '', inputTag); + const prList = await GitUtils.getPullRequestsMergedBetween(priorTag, inputTag); console.log('Found the pull request list: ', prList); core.setOutput('PR_LIST', prList); } catch (error) { diff --git a/.github/actions/javascript/getDeployPullRequestList/index.js b/.github/actions/javascript/getDeployPullRequestList/index.js index 3faaeb28f548..918d631778d3 100644 --- a/.github/actions/javascript/getDeployPullRequestList/index.js +++ b/.github/actions/javascript/getDeployPullRequestList/index.js @@ -11502,111 +11502,51 @@ const github = __importStar(__nccwpck_require__(5438)); const ActionUtils_1 = __nccwpck_require__(6981); const GithubUtils_1 = __importDefault(__nccwpck_require__(9296)); const GitUtils_1 = __importDefault(__nccwpck_require__(1547)); -const BUILD_AND_DEPLOY_JOB_NAME_PREFIX = 'Build and deploy'; -/** - * This function checks if a given release is a valid baseTag to get the PR list with `git log baseTag...endTag`. - * - * The rules are: - * - production deploys can only be compared with other production deploys - * - staging deploys can be compared with other staging deploys or production deploys. - * The reason is that the final staging release in each deploy cycle will BECOME a production release. - * For example, imagine a checklist is closed with version 9.0.20-6; that's the most recent staging deploy, but the release for 9.0.20-6 is now finalized, so it looks like a prod deploy. - * When 9.0.21-0 finishes deploying to staging, the most recent prerelease is 9.0.20-5. However, we want 9.0.20-6...9.0.21-0, - * NOT 9.0.20-5...9.0.21-0 (so that the PR CP'd in 9.0.20-6 is not included in the next checklist) - */ -async function isReleaseValidBaseForEnvironment(releaseTag, isProductionDeploy) { - if (!isProductionDeploy) { - return true; - } - const isPrerelease = (await GithubUtils_1.default.octokit.repos.getReleaseByTag({ - owner: github.context.repo.owner, - repo: github.context.repo.repo, - tag: releaseTag, - })).data.prerelease; - return !isPrerelease; -} -/** - * Was a given deploy workflow run successful on at least one platform? - */ -async function wasDeploySuccessful(runID) { - const jobsForWorkflowRun = (await GithubUtils_1.default.octokit.actions.listJobsForWorkflowRun({ - owner: github.context.repo.owner, - repo: github.context.repo.repo, - // eslint-disable-next-line @typescript-eslint/naming-convention - run_id: runID, - filter: 'latest', - })).data.jobs; - return jobsForWorkflowRun.some((job) => job.name.startsWith(BUILD_AND_DEPLOY_JOB_NAME_PREFIX) && job.conclusion === 'success'); -} -/** - * This function checks if a given deploy workflow is a valid basis for comparison when listing PRs merged between two versions. - * It returns the reason a version should be skipped, or an empty string if the version should not be skipped. - */ -async function shouldSkipVersion(lastSuccessfulDeploy, inputTag, isProductionDeploy) { - if (!lastSuccessfulDeploy?.head_branch) { - // This should never happen. Just doing this to appease TS. - return ''; - } - // we never want to compare a tag with itself. This check is necessary because prod deploys almost always have the same version as the last staging deploy. - // In this case, the next for wrong environment fails because the release that triggered that staging deploy is now finalized, so it looks like a prod deploy. - if (lastSuccessfulDeploy?.head_branch === inputTag) { - return `Same as input tag ${inputTag}`; - } - if (!(await isReleaseValidBaseForEnvironment(lastSuccessfulDeploy?.head_branch, isProductionDeploy))) { - return 'Was a staging deploy, we only want to compare with other production deploys'; - } - if (!(await wasDeploySuccessful(lastSuccessfulDeploy.id))) { - return 'Was an unsuccessful deploy, nothing was deployed in that version'; - } - return ''; -} async function run() { try { const inputTag = core.getInput('TAG', { required: true }); const isProductionDeploy = !!(0, ActionUtils_1.getJSONInput)('IS_PRODUCTION_DEPLOY', { required: false }, false); const deployEnv = isProductionDeploy ? 'production' : 'staging'; console.log(`Looking for PRs deployed to ${deployEnv} in ${inputTag}...`); - const platformDeploys = (await GithubUtils_1.default.octokit.actions.listWorkflowRuns({ + let priorTag; + let foundCurrentRelease = false; + await GithubUtils_1.default.paginate(GithubUtils_1.default.octokit.repos.listReleases, { owner: github.context.repo.owner, repo: github.context.repo.repo, // eslint-disable-next-line @typescript-eslint/naming-convention - workflow_id: 'platformDeploy.yml', - status: 'completed', - })).data.workflow_runs - // Note: we filter out cancelled runs instead of looking only for success runs - // because if a build fails on even one platform, then it will have the status 'failure' - .filter((workflowRun) => workflowRun.conclusion !== 'cancelled'); - const deploys = (await GithubUtils_1.default.octokit.actions.listWorkflowRuns({ - owner: github.context.repo.owner, - repo: github.context.repo.repo, - // eslint-disable-next-line @typescript-eslint/naming-convention - workflow_id: 'deploy.yml', - status: 'completed', - })).data.workflow_runs - // Note: we filter out cancelled runs instead of looking only for success runs - // because if a build fails on even one platform, then it will have the status 'failure' - .filter((workflowRun) => workflowRun.conclusion !== 'cancelled'); - // W've combined platformDeploy.yml and deploy.yml - // TODO: Remove this once there are successful staging and production deploys using the new deploy.yml workflow - const completedDeploys = [...deploys, ...platformDeploys]; - completedDeploys.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); - // Find the most recent deploy workflow targeting the correct environment, for which at least one of the build jobs finished successfully - let lastSuccessfulDeploy = completedDeploys.shift(); - if (!lastSuccessfulDeploy) { - throw new Error('Could not find a prior successful deploy'); - } - let reason = await shouldSkipVersion(lastSuccessfulDeploy, inputTag, isProductionDeploy); - while (lastSuccessfulDeploy && reason) { - console.log(`Deploy of tag ${lastSuccessfulDeploy.head_branch} was not valid as a base for comparison, looking at the next one. Reason: ${reason}`, lastSuccessfulDeploy.html_url); - lastSuccessfulDeploy = completedDeploys.shift(); - if (!lastSuccessfulDeploy) { - throw new Error('Could not find a prior successful deploy'); + per_page: 100, + }, ({ data }, done) => { + // For production deploys, look only at other production deploys. + // staging deploys can be compared with other staging deploys or production deploys. + // The reason is that the final staging release in each deploy cycle will BECOME a production release + const filteredData = isProductionDeploy ? data.filter((release) => !release.prerelease) : data; + // Release was in the last page, meaning the previous release is the first item in this page + if (foundCurrentRelease) { + priorTag = data.at(0)?.tag_name; + done(); + return filteredData; + } + // Search for the index of input tag + const indexOfCurrentRelease = filteredData.findIndex((release) => release.tag_name === inputTag); + // If it happens to be at the end of this page, then the previous tag will be in the next page. + // Set a flag showing we found it so we grab the first release of the next page + if (indexOfCurrentRelease === filteredData.length - 1) { + foundCurrentRelease = true; + return filteredData; } - reason = await shouldSkipVersion(lastSuccessfulDeploy, inputTag, isProductionDeploy); + // If it's anywhere else in this page, the the prior release is the next item in the page + if (indexOfCurrentRelease >= 0) { + priorTag = filteredData.at(indexOfCurrentRelease + 1)?.tag_name; + done(); + } + // Release not in this page (or we're done) + return filteredData; + }); + if (!priorTag) { + throw new Error('Something went wrong and the prior tag could not be found.'); } - const priorTag = lastSuccessfulDeploy.head_branch; console.log(`Looking for PRs deployed to ${deployEnv} between ${priorTag} and ${inputTag}`); - const prList = await GitUtils_1.default.getPullRequestsMergedBetween(priorTag ?? '', inputTag); + const prList = await GitUtils_1.default.getPullRequestsMergedBetween(priorTag, inputTag); console.log('Found the pull request list: ', prList); core.setOutput('PR_LIST', prList); } diff --git a/.github/actions/javascript/markPullRequestsAsDeployed/action.yml b/.github/actions/javascript/markPullRequestsAsDeployed/action.yml index f0ca77bdbf00..40dfc05e5448 100644 --- a/.github/actions/javascript/markPullRequestsAsDeployed/action.yml +++ b/.github/actions/javascript/markPullRequestsAsDeployed/action.yml @@ -14,7 +14,6 @@ inputs: GITHUB_TOKEN: description: "Github token for authentication" required: true - default: "${{ github.token }}" ANDROID: description: "Android job result ('success', 'failure', 'cancelled', or 'skipped')" required: true @@ -27,6 +26,12 @@ inputs: WEB: description: "Web job result ('success', 'failure', 'cancelled', or 'skipped')" required: true + DATE: + description: "The date of deployment" + required: false + NOTE: + description: "Additional note from the deployer" + required: false runs: using: "node20" main: "./index.js" diff --git a/.github/actions/javascript/markPullRequestsAsDeployed/index.js b/.github/actions/javascript/markPullRequestsAsDeployed/index.js index 9f97e4a72d20..b38b04141395 100644 --- a/.github/actions/javascript/markPullRequestsAsDeployed/index.js +++ b/.github/actions/javascript/markPullRequestsAsDeployed/index.js @@ -12713,9 +12713,15 @@ async function run() { const desktopResult = getDeployTableMessage(core.getInput('DESKTOP', { required: true })); const iOSResult = getDeployTableMessage(core.getInput('IOS', { required: true })); const webResult = getDeployTableMessage(core.getInput('WEB', { required: true })); + const date = core.getInput('DATE'); + const note = core.getInput('NOTE'); function getDeployMessage(deployer, deployVerb, prTitle) { let message = `🚀 [${deployVerb}](${workflowURL}) to ${isProd ? 'production' : 'staging'}`; - message += ` by https://github.com/${deployer} in version: ${version} 🚀`; + message += ` by https://github.com/${deployer} in version: ${version} `; + if (date) { + message += `on ${date}`; + } + message += `🚀`; message += `\n\nplatform | result\n---|---\n🤖 android 🤖|${androidResult}\n🖥 desktop 🖥|${desktopResult}`; message += `\n🍎 iOS 🍎|${iOSResult}\n🕸 web 🕸|${webResult}`; if (deployVerb === 'Cherry-picked' && !/no ?qa/gi.test(prTitle ?? '')) { @@ -12723,6 +12729,9 @@ async function run() { message += '\n\n@Expensify/applauseleads please QA this PR and check it off on the [deploy checklist](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3AStagingDeployCash) if it passes.'; } + if (note) { + message += `\n\n_Note:_ ${note}`; + } return message; } if (isProd) { diff --git a/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts b/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts index 71a5c7d5c6ee..e6424c89833a 100644 --- a/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts +++ b/.github/actions/javascript/markPullRequestsAsDeployed/markPullRequestsAsDeployed.ts @@ -55,9 +55,16 @@ async function run() { const iOSResult = getDeployTableMessage(core.getInput('IOS', {required: true}) as PlatformResult); const webResult = getDeployTableMessage(core.getInput('WEB', {required: true}) as PlatformResult); + const date = core.getInput('DATE'); + const note = core.getInput('NOTE'); + function getDeployMessage(deployer: string, deployVerb: string, prTitle?: string): string { let message = `🚀 [${deployVerb}](${workflowURL}) to ${isProd ? 'production' : 'staging'}`; - message += ` by https://github.com/${deployer} in version: ${version} 🚀`; + message += ` by https://github.com/${deployer} in version: ${version} `; + if (date) { + message += `on ${date}`; + } + message += `🚀`; message += `\n\nplatform | result\n---|---\n🤖 android 🤖|${androidResult}\n🖥 desktop 🖥|${desktopResult}`; message += `\n🍎 iOS 🍎|${iOSResult}\n🕸 web 🕸|${webResult}`; @@ -67,6 +74,10 @@ async function run() { '\n\n@Expensify/applauseleads please QA this PR and check it off on the [deploy checklist](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3AStagingDeployCash) if it passes.'; } + if (note) { + message += `\n\n_Note:_ ${note}`; + } + return message; } diff --git a/.github/libs/promiseWhile.ts b/.github/libs/promiseWhile.ts index 8bedceb894fd..bdd5713be43a 100644 --- a/.github/libs/promiseWhile.ts +++ b/.github/libs/promiseWhile.ts @@ -1,3 +1,4 @@ +// eslint-disable-next-line lodash/import-scope import type {DebouncedFunc} from 'lodash'; /** diff --git a/.github/scripts/buildActions.sh b/.github/scripts/buildActions.sh index ea675aef5634..ae8d87b38341 100755 --- a/.github/scripts/buildActions.sh +++ b/.github/scripts/buildActions.sh @@ -12,7 +12,6 @@ declare -r GITHUB_ACTIONS=( "$ACTIONS_DIR/awaitStagingDeploys/awaitStagingDeploys.ts" "$ACTIONS_DIR/bumpVersion/bumpVersion.ts" "$ACTIONS_DIR/checkDeployBlockers/checkDeployBlockers.ts" - "$ACTIONS_DIR/checkReactCompiler/checkReactCompiler.ts" "$ACTIONS_DIR/createOrUpdateStagingDeploy/createOrUpdateStagingDeploy.ts" "$ACTIONS_DIR/getDeployPullRequestList/getDeployPullRequestList.ts" "$ACTIONS_DIR/getPreviousVersion/getPreviousVersion.ts" diff --git a/.github/scripts/createHelpRedirects.sh b/.github/scripts/createHelpRedirects.sh index 1425939ff3ec..76696977de4d 100755 --- a/.github/scripts/createHelpRedirects.sh +++ b/.github/scripts/createHelpRedirects.sh @@ -1,7 +1,7 @@ #!/bin/bash # # Adds new routes to the Cloudflare Bulk Redirects list for communityDot to helpDot -# pages. Does some basic sanity checking. +# pages. Sanity checking is done upstream in the PRs themselves in verifyRedirect.sh. set -e diff --git a/.github/scripts/verifyRedirect.sh b/.github/scripts/verifyRedirect.sh index 05c402ad7766..af9861f40921 100755 --- a/.github/scripts/verifyRedirect.sh +++ b/.github/scripts/verifyRedirect.sh @@ -1,27 +1,31 @@ #!/bin/bash -# HelpDot - Verifies that redirects.csv does not have any duplicates -# Duplicate sourceURLs break redirection on cloudflare pages +# HelpDot - Verifies that redirects.csv does not have any errors that would prevent +# the bulk redirects in Cloudflare from working. This includes: +# Duplicate sourceURLs +# Source URLs containing anchors or URL params +# URLs pointing to themselves +# +# We also prevent adding source or destination URLs outside of an allowed list +# of domains. That's because these redirects run on our zone as a whole, so you +# could add a redirect for sites outside of help/community and Cloudflare would allow it +# and it would work. source scripts/shellUtils.sh declare -r REDIRECTS_FILE="docs/redirects.csv" declare -a ITEMS_TO_ADD -declare -r RED='\033[0;31m' -declare -r GREEN='\033[0;32m' -declare -r NC='\033[0m' - duplicates=$(awk -F, 'a[$1]++{print $1}' $REDIRECTS_FILE) if [[ -n "$duplicates" ]]; then - echo "${RED}duplicate redirects are not allowed: $duplicates ${NC}" + echo "${RED}duplicate redirects are not allowed: $duplicates ${RESET}" exit 1 fi npm run detectRedirectCycle DETECT_CYCLE_EXIT_CODE=$? if [[ DETECT_CYCLE_EXIT_CODE -eq 1 ]]; then - echo -e "${RED}The redirects.csv has a cycle. Please remove the redirect cycle because it will cause an infinite redirect loop ${NC}" + echo -e "${RED}The redirects.csv has a cycle. Please remove the redirect cycle because it will cause an infinite redirect loop ${RESET}" exit 1 fi @@ -46,8 +50,8 @@ while read -r line; do # Basic sanity checking to make sure that the source and destination are in expected # subdomains. - if ! [[ $SOURCE_URL =~ ^https://(community|help)\.expensify\.com ]] || [[ $SOURCE_URL =~ \# ]]; then - error "Found source URL that is not a communityDot or helpDot URL, or contains a '#': $SOURCE_URL" + if ! [[ $SOURCE_URL =~ ^https://(community|help)\.expensify\.com ]] || [[ $SOURCE_URL =~ (\#|\?) ]]; then + error "Found source URL that is not a communityDot or helpDot URL, or contains a '#' or '?': $SOURCE_URL" exit 1 fi @@ -66,9 +70,9 @@ done <<< "$(tail +2 $REDIRECTS_FILE)" # Sanity check that we should actually be running this and we aren't about to delete # every single redirect. if [[ "${#ITEMS_TO_ADD[@]}" -lt 1 ]]; then - error "No items found to add, why are we running?" + error "${RED}No items found to add, why are we running?${RESET}" exit 1 fi -echo -e "${GREEN}The redirects.csv is valid!${NC}" +echo -e "${GREEN}The redirects.csv is valid!${RESET}" exit 0 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f2a3f96b8f67..53afe03720f7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -14,6 +14,7 @@ concurrency: jobs: validateActor: runs-on: ubuntu-latest + timeout-minutes: 90 outputs: IS_DEPLOYER: ${{ fromJSON(steps.isUserDeployer.outputs.IS_DEPLOYER) || github.actor == 'OSBotify' || github.actor == 'os-botify[bot]' }} steps: @@ -28,10 +29,12 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} - createTag: + prep: needs: validateActor - if: ${{ github.ref == 'refs/heads/staging' }} + if: ${{ fromJSON(needs.validateActor.outputs.IS_DEPLOYER) }} runs-on: ubuntu-latest + outputs: + APP_VERSION: ${{ steps.getAppVersion.outputs.VERSION }} steps: - name: Checkout uses: actions/checkout@v4 @@ -46,9 +49,14 @@ jobs: OS_BOTIFY_APP_ID: ${{ secrets.OS_BOTIFY_APP_ID }} OS_BOTIFY_PRIVATE_KEY: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }} + - name: Get app version + id: getAppVersion + run: echo "VERSION=$(jq -r .version < package.json)" >> "$GITHUB_OUTPUT" + - name: Create and push tag + if: ${{ github.ref == 'refs/heads/staging' }} run: | - git tag "$(jq -r .version < package.json)" + git tag ${{ steps.getAppVersion.outputs.VERSION }} git push origin --tags # Note: we're updating the checklist before running the deploys and assuming that it will succeed on at least one platform @@ -56,15 +64,15 @@ jobs: name: Create or update deploy checklist uses: ./.github/workflows/createDeployChecklist.yml if: ${{ github.ref == 'refs/heads/staging' }} - needs: createTag + needs: prep secrets: inherit android: - # WARNING: getDeployPullRequestList depends on this job name. do not change job name without adjusting that action accordingly name: Build and deploy Android - needs: validateActor - if: ${{ fromJSON(needs.validateActor.outputs.IS_DEPLOYER) }} + needs: prep runs-on: ubuntu-latest-xl + env: + RUBYOPT: '-rostruct' steps: - name: Checkout uses: actions/checkout@v4 @@ -96,16 +104,21 @@ jobs: env: LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }} - - name: Set version in ENV - run: echo "VERSION_CODE=$(grep -o 'versionCode\s\+[0-9]\+' android/app/build.gradle | awk '{ print $2 }')" >> "$GITHUB_ENV" + - name: Get Android native version + id: getAndroidVersion + run: echo "VERSION_CODE=$(grep -o 'versionCode\s\+[0-9]\+' android/app/build.gradle | awk '{ print $2 }')" >> "$GITHUB_OUTPUT" - - name: Run Fastlane - run: bundle exec fastlane android ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && 'production' || 'beta' }} + - name: Build Android app + if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + run: bundle exec fastlane android build env: - RUBYOPT: '-rostruct' MYAPP_UPLOAD_STORE_PASSWORD: ${{ secrets.MYAPP_UPLOAD_STORE_PASSWORD }} MYAPP_UPLOAD_KEY_PASSWORD: ${{ secrets.MYAPP_UPLOAD_KEY_PASSWORD }} - VERSION: ${{ env.VERSION_CODE }} + + - name: Upload Android app to Google Play + run: bundle exec fastlane android ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && 'upload_google_play_production' || 'upload_google_play_internal' }} + env: + VERSION: ${{ steps.getAndroidVersion.outputs.VERSION_CODE }} - name: Upload Android build to Browser Stack if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} @@ -141,7 +154,7 @@ jobs: attachments: [{ color: "#DB4545", pretext: ``, - text: `💥 Android production deploy failed. Please manually submit ${{ env.VERSION }} in the . 💥`, + text: `💥 Android production deploy failed. Please manually submit ${{ needs.prep.outputs.APP_VERSION }} in the . 💥`, }] } env: @@ -149,10 +162,8 @@ jobs: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} desktop: - # WARNING: getDeployPullRequestList depends on this job name. do not change job name without adjusting that action accordingly name: Build and deploy Desktop - needs: validateActor - if: ${{ fromJSON(needs.validateActor.outputs.IS_DEPLOYER) }} + needs: prep runs-on: macos-14-large steps: - name: Checkout @@ -185,20 +196,18 @@ jobs: - name: Upload desktop sourcemaps artifact uses: actions/upload-artifact@v4 with: - name: desktop-sourcemaps-artifact + name: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && 'desktop-sourcemaps-artifact' || 'desktop-staging-sourcemaps-artifact' }} path: ./desktop/dist/www/merged-source-map.js.map - name: Upload desktop build artifact uses: actions/upload-artifact@v4 with: - name: desktop-build-artifact + name: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && 'desktop-build-artifact' || 'desktop-staging-build-artifact' }} path: ./desktop-build/NewExpensify.dmg iOS: - # WARNING: getDeployPullRequestList depends on this job name. do not change job name without adjusting that action accordingly name: Build and deploy iOS - needs: validateActor - if: ${{ fromJSON(needs.validateActor.outputs.IS_DEPLOYER) }} + needs: prep env: DEVELOPER_DIR: /Applications/Xcode_15.2.0.app/Contents/Developer runs-on: macos-13-xlarge @@ -260,17 +269,28 @@ jobs: - name: Set current App version in Env run: echo "VERSION=$(npm run print-version --silent)" >> "$GITHUB_ENV" - - name: Set iOS version in ENV - run: echo "IOS_VERSION=$(echo '${{ env.VERSION }}' | tr '-' '.')" >> "$GITHUB_ENV" + - name: Get iOS native version + id: getIOSVersion + run: echo "IOS_VERSION=$(echo '${{ needs.prep.outputs.APP_VERSION }}' | tr '-' '.')" >> "$GITHUB_OUTPUT" - - name: Run Fastlane - run: bundle exec fastlane ios ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && 'production' || 'beta' }} + - name: Build iOS release app + if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + run: bundle exec fastlane ios build + + - name: Upload release build to TestFlight + if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + run: bundle exec fastlane ios upload_testflight env: APPLE_CONTACT_EMAIL: ${{ secrets.APPLE_CONTACT_EMAIL }} APPLE_CONTACT_PHONE: ${{ secrets.APPLE_CONTACT_PHONE }} APPLE_DEMO_EMAIL: ${{ secrets.APPLE_DEMO_EMAIL }} APPLE_DEMO_PASSWORD: ${{ secrets.APPLE_DEMO_PASSWORD }} - VERSION: ${{ env.IOS_VERSION }} + + - name: Submit build for App Store review + if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} + run: bundle exec fastlane ios submit_for_review + env: + VERSION: ${{ steps.getIOSVersion.outputs.IOS_VERSION }} - name: Upload iOS build to Browser Stack if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} @@ -303,7 +323,7 @@ jobs: attachments: [{ color: "#DB4545", pretext: ``, - text: `💥 iOS production deploy failed. Please manually submit ${{ env.IOS_VERSION }} in the . 💥`, + text: `💥 iOS production deploy failed. Please manually submit ${{ steps.getIOSVersion.outputs.IOS_VERSION }} in the . 💥`, }] } env: @@ -311,10 +331,8 @@ jobs: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} web: - # WARNING: getDeployPullRequestList depends on this job name. do not change job name without adjusting that action accordingly name: Build and deploy Web - needs: validateActor - if: ${{ fromJSON(needs.validateActor.outputs.IS_DEPLOYER) }} + needs: prep runs-on: ubuntu-latest-xl steps: - name: Checkout @@ -371,8 +389,8 @@ jobs: run: | sleep 5 DOWNLOADED_VERSION="$(wget -q -O /dev/stdout https://staging.new.expensify.com/version.json | jq -r '.version')" - if [[ '${{ env.VERSION }}' != "$DOWNLOADED_VERSION" ]]; then - echo "Error: deployed version $DOWNLOADED_VERSION does not match local version ${{ env.VERSION }}. Something went wrong..." + if [[ '${{ needs.prep.outputs.APP_VERSION }}' != "$DOWNLOADED_VERSION" ]]; then + echo "Error: deployed version $DOWNLOADED_VERSION does not match local version ${{ needs.prep.outputs.APP_VERSION }}. Something went wrong..." exit 1 fi @@ -381,15 +399,15 @@ jobs: run: | sleep 5 DOWNLOADED_VERSION="$(wget -q -O /dev/stdout https://new.expensify.com/version.json | jq -r '.version')" - if [[ '${{ env.VERSION }}' != "$DOWNLOADED_VERSION" ]]; then - echo "Error: deployed version $DOWNLOADED_VERSION does not match local version ${{ env.VERSION }}. Something went wrong..." + if [[ '${{ needs.prep.outputs.APP_VERSION }}' != "$DOWNLOADED_VERSION" ]]; then + echo "Error: deployed version $DOWNLOADED_VERSION does not match local version ${{ needs.prep.outputs.APP_VERSION }}. Something went wrong..." exit 1 fi - name: Upload web sourcemaps artifact uses: actions/upload-artifact@v4 with: - name: web-sourcemaps-artifact + name: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && 'web' || 'web-staging' }}-sourcemaps-artifact path: ./dist/merged-source-map.js.map - name: Compress web build .tar.gz and .zip @@ -400,13 +418,13 @@ jobs: - name: Upload .tar.gz web build artifact uses: actions/upload-artifact@v4 with: - name: web-build-tar-gz-artifact + name: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && 'web' || 'web-staging' }}-build-tar-gz-artifact path: ./webBuild.tar.gz - name: Upload .zip web build artifact uses: actions/upload-artifact@v4 with: - name: web-build-zip-artifact + name: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && 'web' || 'web-staging' }}-build-zip-artifact path: ./webBuild.zip postSlackMessageOnFailure: @@ -426,64 +444,62 @@ jobs: # Build a version of iOS and Android HybridApp if we are deploying to staging hybridApp: runs-on: ubuntu-latest - needs: validateActor - if: ${{ fromJSON(needs.validateActor.outputs.IS_DEPLOYER) && github.ref == 'refs/heads/staging' }} + needs: prep + if: ${{ github.ref == 'refs/heads/staging' }} steps: - - name: Checkout - uses: actions/checkout@v4 - - name: 'Deploy HybridApp' - run: gh workflow run --repo Expensify/Mobile-Deploy deploy.yml -f force_build=true -f build_version="$(npm run print-version --silent)" + run: gh workflow run --repo Expensify/Mobile-Deploy deploy.yml -f force_build=true -f build_version="${{ needs.prep.outputs.APP_VERSION }}" env: GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} checkDeploymentSuccess: runs-on: ubuntu-latest outputs: - IS_AT_LEAST_ONE_PLATFORM_DEPLOYED: ${{ steps.checkDeploymentSuccess.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED }} - IS_ALL_PLATFORMS_DEPLOYED: ${{ steps.checkDeploymentSuccess.outputs.IS_ALL_PLATFORMS_DEPLOYED }} + IS_AT_LEAST_ONE_PLATFORM_DEPLOYED: ${{ steps.checkDeploymentSuccessOnAtLeastOnePlatform.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED }} + IS_ALL_PLATFORMS_DEPLOYED: ${{ steps.checkDeploymentSuccessOnAtLeastAllPlatform.outputs.IS_ALL_PLATFORMS_DEPLOYED }} needs: [android, desktop, iOS, web] + if: ${{ always() }} steps: - name: Check deployment success on at least one platform - id: checkDeploymentSuccess + id: checkDeploymentSuccessOnAtLeastOnePlatform run: | isAtLeastOnePlatformDeployed="false" - isAllPlatformsDeployed="false" if [ "${{ needs.android.result }}" == "success" ] || \ [ "${{ needs.iOS.result }}" == "success" ] || \ [ "${{ needs.desktop.result }}" == "success" ] || \ [ "${{ needs.web.result }}" == "success" ]; then isAtLeastOnePlatformDeployed="true" fi + echo "IS_AT_LEAST_ONE_PLATFORM_DEPLOYED=$isAtLeastOnePlatformDeployed" >> "$GITHUB_OUTPUT" + echo "IS_AT_LEAST_ONE_PLATFORM_DEPLOYED is $isAtLeastOnePlatformDeployed" + + - name: Check deployment success on all platforms + id: checkDeploymentSuccessOnAtLeastAllPlatform + run: | + isAllPlatformsDeployed="false" if [ "${{ needs.android.result }}" == "success" ] && \ [ "${{ needs.iOS.result }}" == "success" ] && \ [ "${{ needs.desktop.result }}" == "success" ] && \ [ "${{ needs.web.result }}" == "success" ]; then isAllPlatformsDeployed="true" fi - echo "IS_AT_LEAST_ONE_PLATFORM_DEPLOYED=\"$isAtLeastOnePlatformDeployed\"" >> "$GITHUB_OUTPUT" - echo "IS_ALL_PLATFORMS_DEPLOYED=\"$isAllPlatformsDeployed\"" >> "$GITHUB_OUTPUT" + echo "IS_ALL_PLATFORMS_DEPLOYED=$isAllPlatformsDeployed" >> "$GITHUB_OUTPUT" + echo "IS_ALL_PLATFORMS_DEPLOYED is $isAllPlatformsDeployed" createPrerelease: runs-on: ubuntu-latest - if: ${{ github.ref == 'refs/heads/staging' && fromJSON(needs.checkDeploymentSuccess.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED) }} - needs: [checkDeploymentSuccess] + if: ${{ always() && github.ref == 'refs/heads/staging' && fromJSON(needs.checkDeploymentSuccess.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED) }} + needs: [prep, checkDeploymentSuccess] steps: - - name: Checkout staging branch - uses: actions/checkout@v4 - - - name: Get current app version - run: echo "STAGING_VERSION=$(jq -r .version < package.json)" >> "$GITHUB_ENV" - - name: Download all workflow run artifacts uses: actions/download-artifact@v4 - name: 🚀 Create prerelease 🚀 run: | - gh release create ${{ env.STAGING_VERSION }} --title ${{ env.STAGING_VERSION }} --generate-notes --prerelease --target staging + gh release create ${{ needs.prep.outputs.APP_VERSION }} --repo ${{ github.repository }} --title ${{ needs.prep.outputs.APP_VERSION }} --generate-notes --prerelease --target staging RETRIES=0 MAX_RETRIES=10 - until [[ $(gh release view ${{ env.STAGING_VERSION }}) || $RETRIES -ge $MAX_RETRIES ]]; do + until [[ $(gh release view ${{ needs.prep.outputs.APP_VERSION }} --repo ${{ github.repository }}) || $RETRIES -ge $MAX_RETRIES ]]; do echo "release not found, retrying $((MAX_RETRIES - RETRIES++)) times" sleep 1 done @@ -492,21 +508,21 @@ jobs: - name: Rename web and desktop sourcemaps artifacts before assets upload in order to have unique ReleaseAsset.name run: | - mv ./desktop-sourcemaps-artifact/merged-source-map.js.map ./desktop-sourcemaps-artifact/desktop-merged-source-map.js.map - mv ./web-sourcemaps-artifact/merged-source-map.js.map ./web-sourcemaps-artifact/web-merged-source-map.js.map + mv ./desktop-staging-sourcemaps-artifact/merged-source-map.js.map ./desktop-staging-sourcemaps-artifact/desktop-staging-merged-source-map.js.map + mv ./web-staging-sourcemaps-artifact/merged-source-map.js.map ./web-staging-sourcemaps-artifact/web-staging-merged-source-map.js.map - name: Upload artifacts to GitHub Release run: | - gh release upload ${{ env.STAGING_VERSION }} \ - ./android-sourcemaps-artifact/index.android.bundle.map#android-sourcemap-${{ env.STAGING_VERSION }} \ + gh release upload ${{ needs.prep.outputs.APP_VERSION }} --repo ${{ github.repository }} --clobber \ + ./android-sourcemaps-artifact/index.android.bundle.map#android-sourcemap-${{ needs.prep.outputs.APP_VERSION }} \ ./android-build-artifact/app-production-release.aab \ - ./desktop-sourcemaps-artifact/desktop-merged-source-map.js.map#desktop-sourcemap-${{ env.STAGING_VERSION }} \ - ./desktop-build-artifact/NewExpensify.dmg \ - ./ios-sourcemaps-artifact/main.jsbundle.map#ios-sourcemap-${{ env.STAGING_VERSION }} \ + ./desktop-staging-sourcemaps-artifact/desktop-staging-merged-source-map.js.map#desktop-staging-sourcemap-${{ needs.prep.outputs.APP_VERSION }} \ + ./desktop-staging-build-artifact/NewExpensify.dmg#NewExpensifyStaging.dmg \ + ./ios-sourcemaps-artifact/main.jsbundle.map#ios-sourcemap-${{ needs.prep.outputs.APP_VERSION }} \ ./ios-build-artifact/New\ Expensify.ipa \ - ./web-sourcemaps-artifact/web-merged-source-map.js.map#web-sourcemap-${{ env.STAGING_VERSION }} \ - ./web-build-tar-gz-artifact/webBuild.tar.gz \ - ./web-build-zip-artifact/webBuild.zip + ./web-staging-sourcemaps-artifact/web-staging-merged-source-map.js.map#web-staging-sourcemap-${{ needs.prep.outputs.APP_VERSION }} \ + ./web-staging-build-tar-gz-artifact/webBuild.tar.gz#stagingWebBuild.tar.gz \ + ./web-staging-build-zip-artifact/webBuild.zip#stagingWebBuild.zip env: GITHUB_TOKEN: ${{ github.token }} @@ -530,15 +546,9 @@ jobs: finalizeRelease: runs-on: ubuntu-latest - if: ${{ github.ref == 'refs/heads/production' && fromJSON(needs.checkDeploymentSuccess.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED) }} - needs: [checkDeploymentSuccess] + if: ${{ always() && github.ref == 'refs/heads/production' && fromJSON(needs.checkDeploymentSuccess.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED) }} + needs: [prep, checkDeploymentSuccess] steps: - - name: Checkout production branch - uses: actions/checkout@v4 - - - name: Get current app version - run: echo "PRODUCTION_VERSION=$(npm run print-version --silent)" >> "$GITHUB_ENV" - - name: Download all workflow run artifacts uses: actions/download-artifact@v4 @@ -547,22 +557,22 @@ jobs: mv ./desktop-sourcemaps-artifact/merged-source-map.js.map ./desktop-sourcemaps-artifact/desktop-merged-source-map.js.map mv ./web-sourcemaps-artifact/merged-source-map.js.map ./web-sourcemaps-artifact/web-merged-source-map.js.map - - name: Upload artifacts to GitHub Release + - name: 🚀 Edit the release to be no longer a prerelease 🚀 run: | - gh release upload ${{ env.STAGING_VERSION }} \ - ./desktop-sourcemaps-artifact/desktop-merged-source-map.js.map#desktop-sourcemap-${{ env.STAGING_VERSION }} \ - ./desktop-build-artifact/NewExpensify.dmg \ - ./web-sourcemaps-artifact/web-merged-source-map.js.map#web-sourcemap-${{ env.STAGING_VERSION }} \ - ./web-build-tar-gz-artifact/webBuild.tar.gz \ - ./web-build-zip-artifact/webBuild.zip + LATEST_RELEASE="$(gh release list --repo ${{ github.repository }} --exclude-pre-releases --json tagName,isLatest --jq '.[] | select(.isLatest) | .tagName')" + gh api --method POST /repos/Expensify/App/releases/generate-notes -f "tag_name=${{ needs.prep.outputs.APP_VERSION }}" -f "previous_tag_name=$LATEST_RELEASE" | jq -r '.body' >> releaseNotes.md + gh release edit ${{ needs.prep.outputs.APP_VERSION }} --repo ${{ github.repository }} --prerelease=false --latest --notes-file releaseNotes.md env: GITHUB_TOKEN: ${{ github.token }} - - name: 🚀 Edit the release to be no longer a prerelease 🚀 + - name: Upload artifacts to GitHub Release run: | - LATEST_RELEASE="$(gh release list --exclude-pre-releases --json tagName,isLatest --jq '.[] | select(.isLatest) | .tagName')" - gh api --method POST /repos/Expensify/App/releases/generate-notes -f "tag_name=${{ env.PRODUCTION_VERSION }}" -f "previous_tag_name=$LATEST_RELEASE" | jq -r '.body' >> releaseNotes.md - gh release edit ${{ env.PRODUCTION_VERSION }} --prerelease=false --latest --notes-file releaseNotes.md + gh release upload ${{ needs.prep.outputs.APP_VERSION }} --repo ${{ github.repository }} --clobber \ + ./desktop-sourcemaps-artifact/desktop-merged-source-map.js.map#desktop-sourcemap-${{ needs.prep.outputs.APP_VERSION }} \ + ./desktop-build-artifact/NewExpensify.dmg \ + ./web-sourcemaps-artifact/web-merged-source-map.js.map#web-sourcemap-${{ needs.prep.outputs.APP_VERSION }} \ + ./web-build-tar-gz-artifact/webBuild.tar.gz \ + ./web-build-zip-artifact/webBuild.zip env: GITHUB_TOKEN: ${{ github.token }} @@ -587,15 +597,9 @@ jobs: postSlackMessageOnSuccess: name: Post a Slack message when all platforms deploy successfully runs-on: ubuntu-latest - if: ${{ fromJSON(needs.checkDeploymentSuccess.outputs.IS_ALL_PLATFORMS_DEPLOYED) }} - needs: [checkDeploymentSuccess, createPrerelease, finalizeRelease] + if: ${{ always() && fromJSON(needs.checkDeploymentSuccess.outputs.IS_ALL_PLATFORMS_DEPLOYED) }} + needs: [prep, android, desktop, iOS, web, checkDeploymentSuccess, createPrerelease, finalizeRelease] steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set current App version in Env - run: echo "VERSION=$(npm run print-version --silent)" >> "$GITHUB_ENV" - - name: 'Announces the deploy in the #announce Slack room' uses: 8398a7/action-slack@v3 with: @@ -605,7 +609,7 @@ jobs: channel: '#announce', attachments: [{ color: 'good', - text: `🎉️ Successfully deployed ${process.env.AS_REPO} to ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && 'production' || 'staging' }} 🎉️`, + text: `🎉️ Successfully deployed ${process.env.AS_REPO} to ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && 'production' || 'staging' }} 🎉️`, }] } env: @@ -621,7 +625,7 @@ jobs: channel: '#deployer', attachments: [{ color: 'good', - text: `🎉️ Successfully deployed ${process.env.AS_REPO} to ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && 'production' || 'staging' }} 🎉️`, + text: `🎉️ Successfully deployed ${process.env.AS_REPO} to ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && 'production' || 'staging' }} 🎉️`, }] } env: @@ -638,44 +642,21 @@ jobs: channel: '#expensify-open-source', attachments: [{ color: 'good', - text: `🎉️ Successfully deployed ${process.env.AS_REPO} to production 🎉️`, + text: `🎉️ Successfully deployed ${process.env.AS_REPO} to production 🎉️`, }] } env: GITHUB_TOKEN: ${{ github.token }} SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - postGithubComment: - name: Post a GitHub comments on all deployed PRs when platforms are done building and deploying - runs-on: ubuntu-latest - if: ${{ fromJSON(needs.checkDeploymentSuccess.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED) }} - needs: [android, desktop, iOS, web, checkDeploymentSuccess, createPrerelease, finalizeRelease] - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Node - uses: ./.github/actions/composite/setupNode - - - name: Set current App version in Env - run: echo "VERSION=$(npm run print-version --silent)" >> "$GITHUB_ENV" - - - name: Get Release Pull Request List - id: getReleasePRList - uses: ./.github/actions/javascript/getDeployPullRequestList - with: - TAG: ${{ env.VERSION }} - GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} - IS_PRODUCTION_DEPLOY: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} - - - name: Comment on issues - uses: ./.github/actions/javascript/markPullRequestsAsDeployed - with: - PR_LIST: ${{ steps.getReleasePRList.outputs.PR_LIST }} - IS_PRODUCTION_DEPLOY: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }} - DEPLOY_VERSION: ${{ env.VERSION }} - GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }} - ANDROID: ${{ needs.android.result }} - DESKTOP: ${{ needs.desktop.result }} - IOS: ${{ needs.iOS.result }} - WEB: ${{ needs.web.result }} + postGithubComments: + uses: ./.github/workflows/postDeployComments.yml + if: ${{ always() && fromJSON(needs.checkDeploymentSuccess.outputs.IS_AT_LEAST_ONE_PLATFORM_DEPLOYED) }} + needs: [prep, android, desktop, iOS, web, checkDeploymentSuccess, createPrerelease, finalizeRelease] + with: + version: ${{ needs.prep.outputs.APP_VERSION }} + env: ${{ github.ref == 'refs/heads/production' && 'production' || 'staging' }} + android: ${{ needs.android.result }} + ios: ${{ needs.iOS.result }} + web: ${{ needs.web.result }} + desktop: ${{ needs.desktop.result }} diff --git a/.github/workflows/e2ePerformanceTests.yml b/.github/workflows/e2ePerformanceTests.yml index e57556143978..b9352d406feb 100644 --- a/.github/workflows/e2ePerformanceTests.yml +++ b/.github/workflows/e2ePerformanceTests.yml @@ -220,7 +220,7 @@ jobs: Test spec output.txt log_artifacts: debug.log cleanup: true - timeout: 5400 + timeout: 7200 - name: Print logs if run failed if: failure() diff --git a/.github/workflows/lint-changed.yml b/.github/workflows/lint-changed.yml new file mode 100644 index 000000000000..ddb640bfe9cf --- /dev/null +++ b/.github/workflows/lint-changed.yml @@ -0,0 +1,32 @@ +name: Changed files ESLint check + +on: + workflow_call: + pull_request: + types: [opened, synchronize] + branches-ignore: [staging, production] + paths: ['**.js', '**.ts', '**.tsx', '**.json', '**.mjs', '**.cjs', 'config/.editorconfig', '.watchmanconfig', '.imgbotconfig'] + +concurrency: + group: ${{ github.ref == 'refs/heads/main' && format('{0}-{1}', github.ref, github.sha) || github.ref }}-changed-lint + cancel-in-progress: true + +jobs: + lint-changed: + name: Changed files ESLint check + if: ${{ github.actor != 'OSBotify' || github.event_name == 'workflow_call' }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: ./.github/actions/composite/setupNode + + - name: Run ESLint to check for deprecation warnings + run: | + # This will just fetch the latest commit from main + git fetch origin main --no-tags --depth=1 + + # shellcheck disable=SC2046 + npm run lint-changed diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index da7757fcbfa8..af6bfa17fb6e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,4 +1,4 @@ -name: Lint code +name: ESLint check on: workflow_call: @@ -13,7 +13,7 @@ concurrency: jobs: lint: - name: Run ESLint + name: ESLint check if: ${{ github.actor != 'OSBotify' || github.event_name == 'workflow_call' }} runs-on: ubuntu-latest steps: @@ -27,16 +27,3 @@ jobs: run: npm run lint env: CI: true - - - name: Verify there's no Prettier diff - run: | - npm run prettier -- --loglevel silent - if ! git diff --name-only --exit-code; then - # shellcheck disable=SC2016 - echo 'Error: Prettier diff detected! Please run `npm run prettier` and commit the changes.' - exit 1 - fi - - - name: Run unused style searcher - shell: bash - run: ./.github/scripts/findUnusedKeys.sh diff --git a/.github/workflows/postDeployComments.yml b/.github/workflows/postDeployComments.yml new file mode 100644 index 000000000000..3893d3cf3f7c --- /dev/null +++ b/.github/workflows/postDeployComments.yml @@ -0,0 +1,118 @@ +name: Post Deploy Comments + +on: + workflow_call: + inputs: + version: + description: The version that was deployed + required: true + type: string + env: + description: The environment that was deployed (staging or prod) + required: true + type: string + android: + description: Android deploy status + required: true + type: string + ios: + description: iOS deploy status + required: true + type: string + web: + description: Web deploy status + required: true + type: string + desktop: + description: Desktop deploy status + required: true + type: string + workflow_dispatch: + inputs: + version: + description: The version that was deployed + required: true + type: string + env: + description: The environment that was deployed (staging or prod) + required: true + type: choice + options: + - staging + - production + android: + description: Android deploy status + required: true + type: choice + options: + - success + - failure + - cancelled + - skipped + ios: + description: iOS deploy status + required: true + type: choice + options: + - success + - failure + - cancelled + - skipped + web: + description: Web deploy status + required: true + type: choice + options: + - success + - failure + - cancelled + - skipped + desktop: + description: Desktop deploy status + required: true + type: choice + options: + - success + - failure + - cancelled + - skipped + date: + description: The date when this deploy occurred + required: false + type: string + note: + description: Any additional note you want to include with the deploy comment + required: false + type: string + +jobs: + postDeployComments: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: ./.github/actions/composite/setupNode + + - name: Get pull request list + id: getPullRequestList + uses: ./.github/actions/javascript/getDeployPullRequestList + with: + TAG: ${{ inputs.version }} + GITHUB_TOKEN: ${{ github.token }} + IS_PRODUCTION_DEPLOY: ${{ inputs.env == 'production' }} + + - name: Comment on issues + uses: ./.github/actions/javascript/markPullRequestsAsDeployed + with: + PR_LIST: ${{ steps.getPullRequestList.outputs.PR_LIST }} + IS_PRODUCTION_DEPLOY: ${{ inputs.env == 'production' }} + DEPLOY_VERSION: ${{ inputs.version }} + GITHUB_TOKEN: ${{ github.token }} + ANDROID: ${{ inputs.android }} + DESKTOP: ${{ inputs.desktop }} + IOS: ${{ inputs.ios }} + WEB: ${{ inputs.web }} + DATE: ${{ inputs.date }} + NOTE: ${{ inputs.note }} diff --git a/.github/workflows/preDeploy.yml b/.github/workflows/preDeploy.yml index e5ccdfa53076..796468170275 100644 --- a/.github/workflows/preDeploy.yml +++ b/.github/workflows/preDeploy.yml @@ -13,6 +13,9 @@ jobs: lint: uses: ./.github/workflows/lint.yml + prettier: + uses: ./.github/workflows/prettier.yml + test: uses: ./.github/workflows/test.yml diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml new file mode 100644 index 000000000000..d5cff2d9b838 --- /dev/null +++ b/.github/workflows/prettier.yml @@ -0,0 +1,37 @@ +name: Prettier check + +on: + workflow_call: + pull_request: + types: [opened, synchronize] + branches-ignore: [staging, production] + paths: ['**.js', '**.ts', '**.tsx', '**.json', '**.mjs', '**.cjs', 'config/.editorconfig', '.watchmanconfig', '.imgbotconfig'] + +concurrency: + group: ${{ github.ref == 'refs/heads/main' && format('{0}-{1}', github.ref, github.sha) || github.ref }}-prettier + cancel-in-progress: true + +jobs: + prettier: + name: Prettier check + if: ${{ github.actor != 'OSBotify' || github.event_name == 'workflow_call' }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: ./.github/actions/composite/setupNode + + - name: Verify there's no Prettier diff + run: | + npm run prettier -- --loglevel silent + if ! git diff --name-only --exit-code; then + # shellcheck disable=SC2016 + echo 'Error: Prettier diff detected! Please run `npm run prettier` and commit the changes.' + exit 1 + fi + + - name: Run unused style searcher + shell: bash + run: ./.github/scripts/findUnusedKeys.sh diff --git a/.github/workflows/reactCompiler.yml b/.github/workflows/reactCompiler.yml deleted file mode 100644 index dc2e1b17d804..000000000000 --- a/.github/workflows/reactCompiler.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: 🔮 React Compiler - -on: - pull_request: - paths: - - ".github/workflows/reactCompiler.yml" - - "src/**" - - "package.json" - -jobs: - check: - name: 🧬 Conformity - runs-on: ubuntu-latest - - steps: - - name: Checkout to target branch - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.base.ref }} - - name: Setup Node - uses: ./.github/actions/composite/setupNode - - name: Get list of compiled files (main) - id: old-list - run: | - RAW_OUTPUT=$(npx react-compiler-healthcheck --json 2>/dev/null) - echo "Raw output: $RAW_OUTPUT" - OLD_LIST=$(echo "$RAW_OUTPUT" | jq -c .) - echo "OLD_LIST=$OLD_LIST" >> "$GITHUB_OUTPUT" - - name: Checkout to current branch - uses: actions/checkout@v4 - - name: Setup Node - uses: ./.github/actions/composite/setupNode - - name: Get list of compiled files (PR) - id: new-list - run: | - RAW_OUTPUT=$(npx react-compiler-healthcheck --json 2>/dev/null) - echo "Raw output: $RAW_OUTPUT" - NEW_LIST=$(echo "$RAW_OUTPUT" | jq -c .) - echo "NEW_LIST=$NEW_LIST" >> "$GITHUB_OUTPUT" - - name: Check for react compiler changes - id: checkReactCompiler - uses: ./.github/actions/javascript/checkReactCompiler - with: - NEW_LIST: ${{ steps.new-list.outputs.NEW_LIST }} - OLD_LIST: ${{ steps.old-list.outputs.OLD_LIST }} diff --git a/.github/workflows/testBuild.yml b/.github/workflows/testBuild.yml index 21f7fcedfe85..f523faf785c0 100644 --- a/.github/workflows/testBuild.yml +++ b/.github/workflows/testBuild.yml @@ -10,6 +10,9 @@ on: types: [opened, synchronize, labeled] branches: ['*ci-test/**'] +env: + PULL_REQUEST_NUMBER: ${{ github.event.number || github.event.inputs.PULL_REQUEST_NUMBER }} + jobs: validateActor: runs-on: ubuntu-latest @@ -35,7 +38,6 @@ jobs: echo "The 'Ready to Build' label is not attached to the PR #${{ env.PULL_REQUEST_NUMBER }}" fi env: - PULL_REQUEST_NUMBER: ${{ github.event.number || github.event.inputs.PULL_REQUEST_NUMBER }} GITHUB_TOKEN: ${{ github.token }} getBranchRef: @@ -64,7 +66,7 @@ jobs: if: ${{ fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }} runs-on: ubuntu-latest-xl env: - PULL_REQUEST_NUMBER: ${{ github.event.number || github.event.inputs.PULL_REQUEST_NUMBER }} + RUBYOPT: '-rostruct' steps: - name: Checkout uses: actions/checkout@v4 @@ -111,17 +113,19 @@ jobs: - name: Configure MapBox SDK run: ./scripts/setup-mapbox-sdk.sh ${{ secrets.MAPBOX_SDK_DOWNLOAD_TOKEN }} - - name: Run Fastlane beta test - id: runFastlaneBetaTest - run: bundle exec fastlane android build_internal + - name: Run AdHoc build + run: bundle exec fastlane android build_adhoc + env: + MYAPP_UPLOAD_STORE_PASSWORD: ${{ secrets.MYAPP_UPLOAD_STORE_PASSWORD }} + MYAPP_UPLOAD_KEY_PASSWORD: ${{ secrets.MYAPP_UPLOAD_KEY_PASSWORD }} + + - name: Upload AdHoc build to S3 + run: bundle exec fastlane android upload_s3 env: - RUBYOPT: '-rostruct' S3_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY_ID }} S3_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} S3_BUCKET: ad-hoc-expensify-cash S3_REGION: us-east-1 - MYAPP_UPLOAD_STORE_PASSWORD: ${{ secrets.MYAPP_UPLOAD_STORE_PASSWORD }} - MYAPP_UPLOAD_KEY_PASSWORD: ${{ secrets.MYAPP_UPLOAD_KEY_PASSWORD }} - name: Upload Artifact uses: actions/upload-artifact@v4 @@ -134,7 +138,6 @@ jobs: needs: [validateActor, getBranchRef] if: ${{ fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }} env: - PULL_REQUEST_NUMBER: ${{ github.event.number || github.event.inputs.PULL_REQUEST_NUMBER }} DEVELOPER_DIR: /Applications/Xcode_15.2.0.app/Contents/Developer runs-on: macos-13-xlarge steps: @@ -205,8 +208,11 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1 - - name: Run Fastlane - run: bundle exec fastlane ios build_internal + - name: Build AdHoc app + run: bundle exec fastlane ios build_adhoc + + - name: Upload AdHoc build to S3 + run: bundle exec fastlane ios upload_s3 env: S3_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY_ID }} S3_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} @@ -223,8 +229,6 @@ jobs: name: Build and deploy Desktop for testing needs: [validateActor, getBranchRef] if: ${{ fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }} - env: - PULL_REQUEST_NUMBER: ${{ github.event.number || github.event.inputs.PULL_REQUEST_NUMBER }} runs-on: macos-14-large steps: - name: Checkout @@ -268,8 +272,6 @@ jobs: name: Build and deploy Web needs: [validateActor, getBranchRef] if: ${{ fromJSON(needs.validateActor.outputs.READY_TO_BUILD) }} - env: - PULL_REQUEST_NUMBER: ${{ github.event.number || github.event.inputs.PULL_REQUEST_NUMBER }} runs-on: ubuntu-latest-xl steps: - name: Checkout @@ -304,8 +306,6 @@ jobs: name: Post a GitHub comment with app download links for testing needs: [validateActor, getBranchRef, android, iOS, desktop, web] if: ${{ always() }} - env: - PULL_REQUEST_NUMBER: ${{ github.event.number || github.event.inputs.PULL_REQUEST_NUMBER }} steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.storybook/webpack.config.ts b/.storybook/webpack.config.ts index 0aa38f2e3b82..f0fff8bda698 100644 --- a/.storybook/webpack.config.ts +++ b/.storybook/webpack.config.ts @@ -94,6 +94,11 @@ const webpackConfig = ({config}: {config: Configuration}) => { loader: require.resolve('@svgr/webpack'), }); + config.module.rules?.push({ + test: /pdf\.worker\.mjs$/, + type: 'asset/source', + }); + config.plugins.push( new DefinePlugin({ __DEV__: process.env.NODE_ENV === 'development', diff --git a/README.md b/README.md index c8faff111bae..4a691045e7c2 100644 --- a/README.md +++ b/README.md @@ -619,7 +619,30 @@ Some pointers: key to the translation file and use the arrow function version, like so: `nameOfTheKey: ({amount, dateTime}) => "User has sent " + amount + " to you on " + dateTime,`. This is because the order of the phrases might vary from one language to another. - +- When working with translations that involve plural forms, it's important to handle different cases correctly. + + For example: + - zero: Used when there are no items **(optional)**. + - one: Used when there's exactly one item. + - two: Used when there's two items. **(optional)** + - few: Used for a small number of items **(optional)**. + - many: Used for larger quantities **(optional)**. + - other: A catch-all case for other counts or variations. + + Here’s an example of how to implement plural translations: + + messages: () => ({ + zero: 'No messages', + one: 'One message', + two: 'Two messages', + few: (count) => `${count} messages`, + many: (count) => `You have ${count} messages`, + other: (count) => `You have ${count} unread messages`, + }) + + In your code, you can use the translation like this: + + `translate('common.messages', {count: 1});` ---- # Deploying diff --git a/__mocks__/react-native-haptic-feedback.ts b/__mocks__/react-native-haptic-feedback.ts new file mode 100644 index 000000000000..6d20b410d835 --- /dev/null +++ b/__mocks__/react-native-haptic-feedback.ts @@ -0,0 +1,5 @@ +import type HapticFeedback from 'react-native-haptic-feedback'; + +const RNHapticFeedback: typeof HapticFeedback = {trigger: jest.fn()}; + +export default RNHapticFeedback; diff --git a/android/app/build.gradle b/android/app/build.gradle index 3604cf25d1c1..9153966e1d5d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -110,8 +110,8 @@ android { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion multiDexEnabled rootProject.ext.multiDexEnabled - versionCode 1009003015 - versionName "9.0.30-15" + versionCode 1009004004 + versionName "9.0.40-4" // Supported language variants must be declared here to avoid from being removed during the compilation. // This also helps us to not include unnecessary language variants in the APK. resConfigs "en", "es" diff --git a/android/app/src/main/java/com/expensify/chat/MainApplication.kt b/android/app/src/main/java/com/expensify/chat/MainApplication.kt index 26a28d9955a0..2cc8b7780253 100644 --- a/android/app/src/main/java/com/expensify/chat/MainApplication.kt +++ b/android/app/src/main/java/com/expensify/chat/MainApplication.kt @@ -8,6 +8,7 @@ import android.database.CursorWindow import android.os.Process import androidx.multidex.MultiDexApplication import com.expensify.chat.bootsplash.BootSplashPackage +import com.expensify.chat.shortcutManagerModule.ShortcutManagerPackage import com.facebook.react.PackageList import com.facebook.react.ReactApplication import com.facebook.react.ReactNativeHost @@ -29,6 +30,7 @@ class MainApplication : MultiDexApplication(), ReactApplication { PackageList(this).packages.apply { // Packages that cannot be autolinked yet can be added manually here, for example: // add(MyReactNativePackage()); + add(ShortcutManagerPackage()) add(BootSplashPackage()) add(ExpensifyAppPackage()) add(RNTextInputResetPackage()) diff --git a/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java b/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java index 8eff32dedf76..b950921a0cd5 100644 --- a/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java +++ b/android/app/src/main/java/com/expensify/chat/customairshipextender/CustomNotificationProvider.java @@ -8,6 +8,7 @@ import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.content.Context; +import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Canvas; @@ -30,10 +31,13 @@ import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; import androidx.core.app.Person; +import androidx.core.content.pm.ShortcutInfoCompat; +import androidx.core.content.pm.ShortcutManagerCompat; import androidx.core.graphics.drawable.IconCompat; import androidx.versionedparcelable.ParcelUtils; import com.expensify.chat.R; +import com.expensify.chat.shortcutManagerModule.ShortcutManagerUtils; import com.urbanairship.AirshipConfigOptions; import com.urbanairship.json.JsonMap; import com.urbanairship.json.JsonValue; @@ -47,6 +51,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -205,44 +210,47 @@ private void applyMessageStyle(@NonNull Context context, NotificationCompat.Buil // Use the formatted alert message from the backend. Otherwise fallback on the message in the Onyx data. String message = alert != null ? alert : messageData.get("message").getList().get(0).getMap().get("text").getString(); - String conversationName = payload.get("roomName") == null ? "" : payload.get("roomName").getString(""); + String roomName = payload.get("roomName") == null ? "" : payload.get("roomName").getString(""); - // create the Person object who sent the latest report comment + // Create the Person object who sent the latest report comment Bitmap personIcon = fetchIcon(context, avatar); builder.setLargeIcon(personIcon); Person person = createMessagePersonObject(IconCompat.createWithBitmap(personIcon), accountID, name); + ShortcutManagerUtils.addDynamicShortcut(context, reportID, name, accountID, personIcon, person); + // Create latest received message object long createdTimeInMillis = getMessageTimeInMillis(messageData.get("created").getString("")); NotificationCompat.MessagingStyle.Message newMessage = new NotificationCompat.MessagingStyle.Message(message, createdTimeInMillis, person); - // Conversational styling should be applied to groups chats, rooms, and any 1:1 chats with more than one notification (ensuring the large profile image is always shown) - if (!conversationName.isEmpty() || hasExistingNotification) { - // Create the messaging style notification builder for this notification, associating it with the person who sent the report comment - NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle(person) - .setGroupConversation(true) - .setConversationTitle(conversationName); + NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle(person); + // Add all conversation messages to the notification, including the last one we just received. + List messages; + if (hasExistingNotification) { + NotificationCompat.MessagingStyle previousStyle = NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(existingReportNotification.getNotification()); + messages = previousStyle != null ? previousStyle.getMessages() : new ArrayList<>(List.of(recreatePreviousMessage(existingReportNotification))); + } else { + messages = new ArrayList<>(); + } - // Add all conversation messages to the notification, including the last one we just received. - List messages; - if (hasExistingNotification) { - NotificationCompat.MessagingStyle previousStyle = NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification(existingReportNotification.getNotification()); - messages = previousStyle != null ? previousStyle.getMessages() : new ArrayList<>(List.of(recreatePreviousMessage(existingReportNotification))); - } else { - messages = new ArrayList<>(); - } - - // add the last one message we just received. - messages.add(newMessage); + // add the last one message we just received. + messages.add(newMessage); - for (NotificationCompat.MessagingStyle.Message activeMessage : messages) { - messagingStyle.addMessage(activeMessage); - } + for (NotificationCompat.MessagingStyle.Message activeMessage : messages) { + messagingStyle.addMessage(activeMessage); + } - builder.setStyle(messagingStyle); + // Conversational styling should be applied to groups chats, rooms, and any 1:1 chats with more than one notification (ensuring the large profile image is always shown) + if (!roomName.isEmpty()) { + // Create the messaging style notification builder for this notification, associating it with the person who sent the report comment + messagingStyle + .setGroupConversation(true) + .setConversationTitle(roomName); } + builder.setStyle(messagingStyle); + builder.setShortcutId(accountID); // save reportID and person info for future merging builder.addExtras(createMessageExtrasBundle(reportID, person)); diff --git a/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerModule.java b/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerModule.java new file mode 100644 index 000000000000..fdb6d0ba3b97 --- /dev/null +++ b/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerModule.java @@ -0,0 +1,43 @@ +package com.expensify.chat.shortcutManagerModule; + +import static androidx.core.app.NotificationCompat.CATEGORY_MESSAGE; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.core.app.Person; +import androidx.core.content.pm.ShortcutInfoCompat; +import androidx.core.content.pm.ShortcutManagerCompat; +import androidx.core.graphics.drawable.IconCompat; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; + +import java.util.Collections; + +import com.expensify.chat.customairshipextender.CustomNotificationProvider; + +public class ShortcutManagerModule extends ReactContextBaseJavaModule { + private ReactApplicationContext context; + + public ShortcutManagerModule(ReactApplicationContext context) { + super(context); + this.context = context; + } + + @NonNull + @Override + public String getName() { + return "ShortcutManager"; + } + + @ReactMethod + public void removeAllDynamicShortcuts() { + ShortcutManagerUtils.removeAllDynamicShortcuts(context); + } +} diff --git a/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerPackage.java b/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerPackage.java new file mode 100644 index 000000000000..d28f75592d93 --- /dev/null +++ b/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerPackage.java @@ -0,0 +1,29 @@ +package com.expensify.chat.shortcutManagerModule; + +import androidx.annotation.NonNull; + +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ViewManager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ShortcutManagerPackage implements ReactPackage { + + @NonNull + @Override + public List createViewManagers(@NonNull ReactApplicationContext reactContext) { + return Collections.emptyList(); + } + + @NonNull + @Override + public List createNativeModules(@NonNull ReactApplicationContext reactContext) { + List modules = new ArrayList<>(); + modules.add(new ShortcutManagerModule(reactContext)); + return modules; + } +} diff --git a/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerUtils.java b/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerUtils.java new file mode 100644 index 000000000000..5947faaa67c4 --- /dev/null +++ b/android/app/src/main/java/com/expensify/chat/shortcutManagerModule/ShortcutManagerUtils.java @@ -0,0 +1,38 @@ +package com.expensify.chat.shortcutManagerModule; + +import static androidx.core.app.NotificationCompat.CATEGORY_MESSAGE; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; + +import androidx.core.app.Person; +import androidx.core.content.pm.ShortcutInfoCompat; +import androidx.core.content.pm.ShortcutManagerCompat; +import androidx.core.graphics.drawable.IconCompat; + +import java.util.Collections; + +public class ShortcutManagerUtils { + public static void removeAllDynamicShortcuts(Context context) { + ShortcutManagerCompat.removeAllDynamicShortcuts(context); + } + + public static void addDynamicShortcut(Context context, long reportID, String name, String accountID, Bitmap personIcon, Person person) { + Intent intent = new Intent(Intent.ACTION_VIEW, + Uri.parse("new-expensify://r/" + reportID)); + + ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, accountID) + .setShortLabel(name) + .setLongLabel(name) + .setCategories(Collections.singleton(CATEGORY_MESSAGE)) + .setIntent(intent) + .setLongLived(true) + .setPerson(person) + .setIcon(IconCompat.createWithBitmap(personIcon)) + .build(); + ShortcutManagerCompat.pushDynamicShortcut(context, shortcutInfo); + } + +} diff --git a/android/build.gradle b/android/build.gradle index 85e547439cc1..fd3f9997612e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -29,7 +29,7 @@ buildscript { classpath("com.google.firebase:firebase-crashlytics-gradle:2.7.1") classpath("com.google.firebase:perf-plugin:1.4.1") // Fullstory integration - classpath ("com.fullstory:gradle-plugin-local:1.49.0") + classpath ("com.fullstory:gradle-plugin-local:1.52.0") // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/android/gradle.properties b/android/gradle.properties index 87333d20f743..46cd98554d29 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -55,3 +55,5 @@ MYAPP_UPLOAD_KEY_ALIAS=ReactNativeChat-Key-Alias disableFrameProcessors=true android.nonTransitiveRClass=false + +org.gradle.parallel=true diff --git a/assets/animations/BankVault.lottie b/assets/animations/BankVault.lottie new file mode 100644 index 000000000000..82361e8e2209 Binary files /dev/null and b/assets/animations/BankVault.lottie differ diff --git a/assets/animations/GenericEmptyState.lottie b/assets/animations/GenericEmptyState.lottie new file mode 100644 index 000000000000..d62ff9d980bb Binary files /dev/null and b/assets/animations/GenericEmptyState.lottie differ diff --git a/assets/animations/TripsEmptyState.lottie b/assets/animations/TripsEmptyState.lottie new file mode 100644 index 000000000000..8a07a6ad10d5 Binary files /dev/null and b/assets/animations/TripsEmptyState.lottie differ diff --git a/assets/images/Star.svg b/assets/images/Star.svg new file mode 100644 index 000000000000..71fdfde500a0 --- /dev/null +++ b/assets/images/Star.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/assets/images/bookmark.svg b/assets/images/bookmark.svg new file mode 100644 index 000000000000..d7c1a8397b37 --- /dev/null +++ b/assets/images/bookmark.svg @@ -0,0 +1 @@ + diff --git a/assets/images/companyCards/card-amex.svg b/assets/images/companyCards/card-amex.svg new file mode 100644 index 000000000000..0e8b2d22e9b4 --- /dev/null +++ b/assets/images/companyCards/card-amex.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + diff --git a/assets/images/companyCards/card-bank_of_america.svg b/assets/images/companyCards/card-bank_of_america.svg new file mode 100644 index 000000000000..684a6a0a28f5 --- /dev/null +++ b/assets/images/companyCards/card-bank_of_america.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/companyCards/card-bofa.svg b/assets/images/companyCards/card-bofa.svg new file mode 100644 index 000000000000..469142e4d6ff --- /dev/null +++ b/assets/images/companyCards/card-bofa.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + diff --git a/assets/images/companyCards/card-brex.svg b/assets/images/companyCards/card-brex.svg new file mode 100644 index 000000000000..dd19403d5837 --- /dev/null +++ b/assets/images/companyCards/card-brex.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/assets/images/companyCards/card-capital_one.svg b/assets/images/companyCards/card-capital_one.svg new file mode 100644 index 000000000000..0a324710ae5d --- /dev/null +++ b/assets/images/companyCards/card-capital_one.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/companyCards/card-capitalone.svg b/assets/images/companyCards/card-capitalone.svg new file mode 100644 index 000000000000..95948992383b --- /dev/null +++ b/assets/images/companyCards/card-capitalone.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/assets/images/companyCards/card-chase.svg b/assets/images/companyCards/card-chase.svg new file mode 100644 index 000000000000..7bea71bd66ec --- /dev/null +++ b/assets/images/companyCards/card-chase.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + diff --git a/assets/images/companyCards/card-citi.svg b/assets/images/companyCards/card-citi.svg new file mode 100644 index 000000000000..c8d71afd7798 --- /dev/null +++ b/assets/images/companyCards/card-citi.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + diff --git a/assets/images/companyCards/card-expensify.svg b/assets/images/companyCards/card-expensify.svg new file mode 100644 index 000000000000..9fd29b511c7b --- /dev/null +++ b/assets/images/companyCards/card-expensify.svg @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/companyCards/card-mastercard.svg b/assets/images/companyCards/card-mastercard.svg new file mode 100644 index 000000000000..e8d3cf8f4096 --- /dev/null +++ b/assets/images/companyCards/card-mastercard.svg @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/assets/images/companyCards/card-other.svg b/assets/images/companyCards/card-other.svg new file mode 100644 index 000000000000..11ff21285626 --- /dev/null +++ b/assets/images/companyCards/card-other.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/companyCards/card-stripe.svg b/assets/images/companyCards/card-stripe.svg new file mode 100644 index 000000000000..608f067a1854 --- /dev/null +++ b/assets/images/companyCards/card-stripe.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + diff --git a/assets/images/companyCards/card-visa.svg b/assets/images/companyCards/card-visa.svg new file mode 100644 index 000000000000..9e2eae97ba90 --- /dev/null +++ b/assets/images/companyCards/card-visa.svg @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/companyCards/card-wells_fargo.svg b/assets/images/companyCards/card-wells_fargo.svg new file mode 100644 index 000000000000..66402710de97 --- /dev/null +++ b/assets/images/companyCards/card-wells_fargo.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/companyCards/card-wellsfargo.svg b/assets/images/companyCards/card-wellsfargo.svg new file mode 100644 index 000000000000..086f66cc0423 --- /dev/null +++ b/assets/images/companyCards/card-wellsfargo.svg @@ -0,0 +1,57 @@ + + + + + + + + diff --git a/assets/images/companyCards/card=-generic.svg b/assets/images/companyCards/card=-generic.svg new file mode 100644 index 000000000000..61e4296f7779 --- /dev/null +++ b/assets/images/companyCards/card=-generic.svg @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/assets/images/companyCards/pendingstate_laptop-with-hourglass-and-cards.svg b/assets/images/companyCards/pendingstate_laptop-with-hourglass-and-cards.svg new file mode 100644 index 000000000000..0f40859c8839 --- /dev/null +++ b/assets/images/companyCards/pendingstate_laptop-with-hourglass-and-cards.svg @@ -0,0 +1,244 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/expensify-card-icon.svg b/assets/images/expensify-card-icon.svg index 8e20d27af48c..8680b7a22878 100644 --- a/assets/images/expensify-card-icon.svg +++ b/assets/images/expensify-card-icon.svg @@ -1 +1,16 @@ - \ No newline at end of file + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/expensify-card.svg b/assets/images/expensify-card.svg index 52f55778b2bd..2989f5025ae4 100644 --- a/assets/images/expensify-card.svg +++ b/assets/images/expensify-card.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/assets/images/table.svg b/assets/images/table.svg index a9cfe68f339e..36d4ced774f1 100644 --- a/assets/images/table.svg +++ b/assets/images/table.svg @@ -1,3 +1,3 @@ - + diff --git a/assets/images/user-eye.svg b/assets/images/user-eye.svg new file mode 100644 index 000000000000..2265b4892ded --- /dev/null +++ b/assets/images/user-eye.svg @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/assets/images/user-plus.svg b/assets/images/user-plus.svg new file mode 100644 index 000000000000..bd49633bf738 --- /dev/null +++ b/assets/images/user-plus.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/babel.config.js b/babel.config.js index dcf12ba4d91e..663eb29d5d2f 100644 --- a/babel.config.js +++ b/babel.config.js @@ -8,7 +8,12 @@ const ReactCompilerConfig = { enableTreatRefLikeIdentifiersAsRefs: true, }, }; -const defaultPresets = ['@babel/preset-react', '@babel/preset-env', '@babel/preset-flow', '@babel/preset-typescript']; +/** + * Setting targets to node 20 to reduce JS bundle size + * It is also recommended by babel: + * https://babeljs.io/docs/options#no-targets + */ +const defaultPresets = ['@babel/preset-react', ['@babel/preset-env', {targets: {node: 20}}], '@babel/preset-flow', '@babel/preset-typescript']; const defaultPlugins = [ ['babel-plugin-react-compiler', ReactCompilerConfig], // must run first! // Adding the commonjs: true option to react-native-web plugin can cause styling conflicts @@ -16,10 +21,11 @@ const defaultPlugins = [ '@babel/transform-runtime', '@babel/plugin-proposal-class-properties', + ['@babel/plugin-transform-object-rest-spread', {useBuiltIns: true, loose: true}], - // We use `transform-class-properties` for transforming ReactNative libraries and do not use it for our own + // We use `@babel/plugin-transform-class-properties` for transforming ReactNative libraries and do not use it for our own // source code transformation as we do not use class property assignment. - 'transform-class-properties', + '@babel/plugin-transform-class-properties', // Keep it last 'react-native-reanimated/plugin', diff --git a/config/webpack/webpack.common.ts b/config/webpack/webpack.common.ts index 33fd9131eca0..1bab57905d0e 100644 --- a/config/webpack/webpack.common.ts +++ b/config/webpack/webpack.common.ts @@ -123,11 +123,18 @@ const getCommonConfiguration = ({file = '.env', platform = 'web'}: Environment): {from: 'node_modules/pdfjs-dist/cmaps/', to: 'cmaps/'}, ], }), - new EnvironmentPlugin({JEST_WORKER_ID: null}), + new EnvironmentPlugin({JEST_WORKER_ID: ''}), new IgnorePlugin({ resourceRegExp: /^\.\/locale$/, contextRegExp: /moment$/, }), + ...(file === '.env.production' || file === '.env.staging' + ? [ + new IgnorePlugin({ + resourceRegExp: /@welldone-software\/why-did-you-render/, + }), + ] + : []), ...(platform === 'web' ? [new CustomVersionFilePlugin()] : []), new DefinePlugin({ ...(platform === 'desktop' ? {} : {process: {env: {}}}), @@ -165,7 +172,7 @@ const getCommonConfiguration = ({file = '.env', platform = 'web'}: Environment): // We are importing this worker as a string by using asset/source otherwise it will default to loading via an HTTPS request later. // This causes issues if we have gone offline before the pdfjs web worker is set up as we won't be able to load it from the server. { - test: new RegExp('node_modules/pdfjs-dist/legacy/build/pdf.worker.js'), + test: new RegExp('node_modules/pdfjs-dist/legacy/build/pdf.worker.mjs'), type: 'asset/source', }, @@ -215,6 +222,7 @@ const getCommonConfiguration = ({file = '.env', platform = 'web'}: Environment): }, resolve: { alias: { + lodash: 'lodash-es', // eslint-disable-next-line @typescript-eslint/naming-convention 'react-native-config': 'react-web-config', // eslint-disable-next-line @typescript-eslint/naming-convention diff --git a/contributingGuides/CONTRIBUTING.md b/contributingGuides/CONTRIBUTING.md index ca9ca23a8577..82e368214223 100644 --- a/contributingGuides/CONTRIBUTING.md +++ b/contributingGuides/CONTRIBUTING.md @@ -4,7 +4,7 @@ Welcome! Thanks for checking out the New Expensify app and taking the time to co ## Getting Started If you would like to become an Expensify contributor, the first step is to read this document in its **entirety**. The second step is to review the README guidelines [here](https://github.com/Expensify/App/blob/main/README.md) to understand our coding philosophy and for a general overview of the code repository (i.e. how to run the app locally, testing, storage, our app philosophy, etc). Please read both documents before asking questions, as it may be covered within the documentation. -#### Test Accounts +### Test Accounts You can create as many accounts as needed in order to test your changes directly from [the app](https://new.expensify.com/). An initial account can be created when logging in for the first time, and additional accounts can be created by opening the "New Chat" or "Group Chat" pages via the Global Create menu, inputting a valid email or phone number, and tapping the user's avatar. Do not use Expensify employee or customer accounts for testing. **Notes**: @@ -12,17 +12,17 @@ You can create as many accounts as needed in order to test your changes directly 1. When testing chat functionality in the app please do this between accounts you or your fellow contributors own - **do not test chatting with Concierge**, as this diverts to our customer support team. Thank you. 2. A member of our customer onboarding team gets auto-assigned to every new policy created by a non-paying account to help them set up. Please **do not interact with these teams, ask for calls, or support on your issues.** If you do need to test functionality inside the defaultRooms (#admins & #announce) for any issues you’re working on, please let them know that you are a contributor and don’t need assistance. They will proceed to ignore the chat. -##### Generating Multiple Test Accounts +#### Generating Multiple Test Accounts You can generate multiple test accounts by using a `+` postfix, for example if your email is test@test.com, you can create multiple New Expensify accounts connected to the same email address by using test+123@test.com, test+456@test.com, etc. -##### High Traffic Accounts +#### High Traffic Accounts All internal engineers, contributors, and C+ members are required to test with a “high traffic” account against the staging or production web servers. Use these Google forms to manage your high-traffic accounts. You'll need to authenticate via Google first. 1. [Make an account high-traffic](https://docs.google.com/forms/d/e/1FAIpQLScpiS0Mo-HA5xHPsvDow79yTsMBgF0wjuqc0K37lTK5fheB8Q/viewform) 2. [Remove a high-traffic account](https://docs.google.com/forms/d/e/1FAIpQLSd9_FDav83pnhhtu1KGAKIpf2yttQ_0Bvq1b9nuFM1-wbL11Q/viewform) -#### Working on beta features +### Working on beta features Some features are locked behind beta flags while development is ongoing. As a contributor you can work on these beta features locally by overriding the [`Permissions.canUseAllBetas` function](https://github.com/Expensify/App/blob/5e268df7f2989ed04bc64c0c86ed77faf134554d/src/libs/Permissions.js#L10-L12) to return `true`. ## Code of Conduct @@ -67,35 +67,76 @@ The 168 hours (aka 7 days) will be measured by calculating the time between when ## Finding Jobs A job could be fixing a bug or working on a new feature. There are two ways you can find a job that you can contribute to: -#### Finding a job that Expensify posted +### Finding a job that Expensify posted This is the most common scenario for contributors. The Expensify team posts new jobs to the Upwork job list [here](https://www.upwork.com/nx/search/jobs/?nbs=1&q=expensify%20react%20native&sort=recency&user_location_match=2) (you must be signed in to Upwork to view jobs). Each job in Upwork has a corresponding GitHub issue, which will include instructions to follow. You can also view all open jobs in the Expensify/App GH repository by searching for GH issues with the [`Help Wanted` label](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22). Lastly, you can follow the [@ExpensifyOSS](https://twitter.com/ExpensifyOSS) Twitter account to see a live feed of jobs that are posted. ->**Note:** Our problem solving approach at Expensify is to focus on high value problems and avoid small optimizations with results that are difficult to measure. We also prefer to identify and solve problems at their root. Given that, please ensure all proposed jobs fix a specific problem in a measurable way with evidence so they are easy to evaluate. Here's an example of a good problem/solution: +### Posting proposals for new projects +Our problem solving approach at Expensify is to focus on high value problems and avoid small optimizations with results that are difficult to measure. We also prefer to identify and solve problems at their root. Given that, please ensure all proposed jobs fix a specific problem in a measurable way with evidence so they are easy to evaluate. If you want to propose that Expensify implement some idea you have, you should post it in the #expensify-open-source slack room with the `Strategy`/`Problem`/`Solution` format. + +#### How to write a good problem statement +A good problem statement is in this format: + +> **Problem**: When X happens, it causes Y, which prevents us from Z. + +Just state the direct cause and effect, with minimal fluff and analysis. + +In short, a good problem statement makes no mention whatsoever of the desired solution. This sounds obvious, but is easier said than done. The point of not mentioning the solution is to force you to identify the actual problem you are trying to solve, and not waving your hands with a "reverse solution statement". For example: + +**Bad:** +> **Problem:** We don't have a car +> +> **Solution:** Buy a car. + + +**Good:** +> **Problem:** I want to buy a new chair, but it's too heavy for me to carry it home, so I can't sit in it. +> +> **Solution:** Buy a truck. > ->**Problem:** The app start up time has regressed because we introduced "New Feature" in PR #12345 and is now 1042ms slower because `SomeComponent` is re-rendering 42 times. +> **Solution:** Rent a truck. > ->**Solution:** Start up time will perceptibly decrease by 1042ms if we prevent the unnecessary re-renders of this component. +> **Solution:** Hire movers. +> +> **Solution:** Buy a bean bag. -## Working on Expensify Jobs -*Reminder: For technical guidance, please refer to the [README](https://github.com/Expensify/App/blob/main/README.md)*. -## Posting Ideas +A real problem description enables a much richer discussion to find more creative solutions. Having multiple viable solutions is a good indicator that your problem statement is *NOT* a reverse solution description. + +#### How to write a bad problem statement + +Similarly, a sign that you are actually doing a reverse solution statement is if it contains phrases like: + +* **Problem:** We don't do/have X; **Solution:** Build X + * Rewrite the problem statement to explain the consequence of not doing/having X. You might think it is incredibly obvious -- so obvious that it doesn't need to be stated. But just state it outright. An obvious problem statement is the goal: this is not a place to be clever, it's a place to be clear. +* **Problem:** We lack insight/knowledge/awareness/visibility; **Solution:** Create insight + * Information isn't itself valuable. Not having it isn't inherently a problem; having it isn't itself a solution. Information is only valuable when it enables us to act -- so the problem isn't the lack of information, but the problem is that there is something we aren't doing. Part of the solution might be getting more information -- but the information isn't the whole solution, the solution is that we are doing something with that information that we weren't doing before. +* **Problem:** There are problems A, B, C, D, and E. **Solution:** Do F. + * Focus on one problem at a time, to enable a collaborative conversation about each of the problems in isolation. Are all of A-E equally important? If one stands out, lead with it and focus on it. Otherwise, it's likely you are just "fitting the problem to the solution" and trying to invent a justification for a solution you've already mentally committed to, and aren't genuinely trying to create a collaborative, problem-focused solution. +* **Problem:** X is inefficient/error-prone. **Solution:** Do Y. + * Everything is inefficient and error prone. Everything is cumbersome. Without some kind of quantification, they are not problems, they are just statements of reality. Without some kind of measurable improvement, it's not a solution: it's just a change. Focus on the actual, tangible, measurable problems that can be provably solved. + +Basically, a bad problem statement (ie, a reverse solution statement) is written in such a fashion that it only allows for a single solution. To enable the most creative, most collaborative discussion, equip your peers with the tools to engage by listing all the key assumptions that went into your understanding of the problem, and connecting that directly to the solution. + +### Posting Ideas Additionally, if you want to discuss an idea with the open source community without having a P/S statement yet, you can post it in #expensify-open-source with the prefix `IDEA:`. All ideas to build the future of Expensify are always welcome! i.e.: "`IDEA:` I don't have a P/S for this yet, but just kicking the idea around... what if we [insert crazy idea]?". -#### Make sure you can test on all platforms +## Working on Expensify Jobs +*Reminder: For technical guidance, please refer to the [README](https://github.com/Expensify/App/blob/main/README.md)*. + +### Make sure you can test on all platforms * Expensify requires that you can test the app on iOS, MacOS, Android, Web, and mWeb. * You'll need a Mac to test the iOS and MacOS app. * In case you don't have one, here's a helpful [document](https://github.com/Expensify/App/blob/main/contributingGuides/TESTING_MACOS_AND_IOS.md) on how you might test all platforms on a Windows/Linux device. -#### Check GitHub for existing proposals from other users +### Check GitHub for existing proposals from other users 1. Expensify reviews all solution proposals on a first come first serve basis. If you see other contributors have already proposed a solution, you can still provide a solution proposal and we will review it. We look for the earliest provided, best proposed solution that addresses the job. -#### Make sure you can reproduce the problem +### Make sure you can reproduce the problem 2. Use your test account(s) to reproduce the problem by following the steps in the GitHub issue. 3. If you cannot reproduce the problem, pause on this step and add a comment to the issue explaining where you are stuck or that you don't think the issue can be reproduced. -#### Propose a solution for the job +### Propose a solution for the job 4. You can propose solutions on any issue at any time, but if you propose solutions to jobs before the `Help Wanted` label is applied, you do so at your own risk. Proposals will not be reviewed until the label is added and there is always a chance that we might not add the label or hire an external contributor for the job. 5. Contributors should **not** submit proposals on issues when they have assigned issues or PRs that are awaiting an action from them. If so, they will be in violation of Rule #1 (Get Shit Done) in our [Code of Conduct](https://github.com/Expensify/App/blob/main/CODE_OF_CONDUCT.md) and will receive a warning. Multiple warnings can lead to removal from the program. 6. After you reproduce the issue, complete the [proposal template here](./PROPOSAL_TEMPLATE.md) and post it as a comment in the corresponding GitHub issue (linked in the Upwork job). @@ -111,7 +152,7 @@ Additionally, if you want to discuss an idea with the open source community with 8. If your proposal is accepted by the Expensify engineer assigned to the issue, Expensify will hire you on Upwork and assign the GitHub issue to you. 9. Once hired, post a comment in the Github issue stating when you expect to have your PR ready for review. -#### Begin coding your solution in a pull request +### Begin coding your solution in a pull request 9. When you are ready to start, fork the repository and create a new branch. 10. Before you begin writing any code, please be aware that we require all commits to be [signed](https://docs.github.com/en/github/authenticating-to-github/signing-commits). The easiest way to do that is to [generate a new GPG key](https://docs.github.com/en/github/authenticating-to-github/generating-a-new-gpg-key) and [add it to your GitHub account](https://docs.github.com/en/github/authenticating-to-github/adding-a-new-gpg-key-to-your-github-account). Once you've done that, you can automatically sign all your commits by adding the following to your `.gitconfig`: ``` @@ -124,11 +165,11 @@ Additionally, if you want to discuss an idea with the open source community with [gpg] program = gpg ``` -11. [Open a pull request](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork), and make sure to fill in the required fields. +11. [Open a pull request](https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork). It is required to complete every step and check every box in the PR Author Checklist. If a box has been checked without the action being taken, it will be a violation of [Rule #2](https://github.com/Expensify/App/blob/main/CODE_OF_CONDUCT.md) and could lead to [a warning](https://github.com/Expensify/App/blob/main/CODE_OF_CONDUCT.md#compliance) being issued. 12. An Expensify engineer and a member from the Contributor-Plus team will be assigned to your pull request automatically to review. 13. Daily updates on weekdays are highly recommended. If you know you won’t be able to provide updates within 48 hours, please comment on the PR or issue stating how long you plan to be out so that we may plan accordingly. We understand everyone needs a little vacation here and there. Any issue that doesn't receive an update for 5 days (including weekend days) may be considered abandoned and the original contract terminated. -#### Submit your pull request for final review +### Submit your pull request for final review 14. When you are ready to submit your pull request for final review, make sure the following checks pass: 1. CLA - You must sign our [Contributor License Agreement](https://github.com/Expensify/App/blob/main/contributingGuides/CLA.md) by following the CLA bot instructions that will be posted on your PR 2. Tests - All tests must pass before a merge of a pull request @@ -138,7 +179,7 @@ Additionally, if you want to discuss an idea with the open source community with 17. Upon submission of a PR, please include a numbered list of explicit testing steps for each platform (Web, Desktop, iOS, Android, and Mobile Web) to confirm the fix works as expected and there are no regressions. 18. Please add a screenshot of the app running on each platform (Web, Desktop, iOS, Android, Mobile Web). -#### Completing the final checklist +### Completing the final checklist 19. Once your PR has been deployed to production, a checklist will automatically be commented in the GH issue. You're required to complete the steps that have your name mentioned before payment will be issued. 20. The items requiring your completion consist of: 1. Proposing steps to take for a regression test to ensure the bug doesn't occur again (For information on how to successfully complete this, head [here](https://github.com/Expensify/App/blob/main/contributingGuides/REGRESSION_TEST_BEST_PRACTICES.md)). @@ -147,23 +188,23 @@ Additionally, if you want to discuss an idea with the open source community with 4. Starting a conversation on if any additional steps should be taken to prevent further bugs similar to the one fixed from occurring again. 21. Once the above items have been successfully completed, then payments will begin to be issued. -#### Timeline expectations and asking for help along the way +### Timeline expectations and asking for help along the way - If you have made a change to your pull request and are ready for another review, leave a comment that says "Updated" on the pull request itself. - Please keep the conversation in GitHub, and do not ping individual reviewers in Slack or Upwork to get their attention. - Pull Request reviews can sometimes take a few days. If your pull request has not been addressed after four days, please let us know via the #expensify-open-source Slack channel. - On occasion, our engineers will need to focus on a feature release and choose to place a hold on the review of your PR. -#### Important note about JavaScript Style +### Important note about JavaScript Style - Read our official [JavaScript and React style guide](https://github.com/Expensify/App/blob/main/contributingGuides/STYLE.md). Please refer to our Style Guide before asking for a review. -#### For external agencies that Expensify partners with +### For external agencies that Expensify partners with Follow all the above above steps and processes. When you find a job you'd like to work on: - Post “I’m from [agency], I’d like to work on this job” - If no proposals have been submitted by other contributors, BugZero (BZ) team member or an internal engineer will assign the issue to you. - If there are existing proposals, BZ will put the issue on hold. [Contributor+](https://github.com/Expensify/App/blob/main/contributingGuides/HOW_TO_BECOME_A_CONTRIBUTOR_PLUS.md) will review the existing proposals. If a contributor’s proposal is accepted then the contributor will be assigned to the issue. If not the issue will be assigned to the agency-employee. - Once assigned follow the steps [here](https://github.com/Expensify/App/blob/main/contributingGuides/CONTRIBUTING.md#propose-a-solution-for-the-job) to submit your proposal -#### Guide on Acronyms used within Expensify Communication +## Guide on Acronyms used within Expensify Communication During communication with Expensify, you will come across a variety of acronyms used by our team. While acronyms can be useful, they cease to be the moment they are not known to the receiver. As such, we wanted to create a list here of our most commonly used acronyms and what they're referring to. Lastly, please never hesitate to ask in Slack or the GH issue if there are any that are not understood/known! - **ND/NewDot:** new.expensify.com - **OD/OldDot:** expensify.com diff --git a/contributingGuides/REACT_COMPILER.md b/contributingGuides/REACT_COMPILER.md index 520cbd7b164a..93477fcfc9d0 100644 --- a/contributingGuides/REACT_COMPILER.md +++ b/contributingGuides/REACT_COMPILER.md @@ -6,13 +6,9 @@ At Expensify, we are early adopters of this tool and aim to fully leverage its capabilities. -## React Compiler CI check +## React Compiler compatibility check -We have implemented a CI check that runs the React Compiler on all pull requests (PRs). This check compares compilable files from the PR branch with those in the target branch. If it detects that a file was previously compiled successfully but now fails to compile, the check will fail. - -## What if CI check fails in my PR? - -If the CI check fails for your PR, you need to fix the problem. If you're unsure how to resolve it, you can ask for help in the `#expensify-open-source` Slack channel (and tag `@Kiryl Ziusko`). +To check if your code can be compiled by React Compiler and hence gets all its optimizations "for free", you can run the `npm run react-compiler-healthcheck-test` locally and analyze the output. ## How can I check what exactly prevents file from successful optimization or whether my fix for passing `react-compiler` actually works? diff --git a/contributingGuides/STYLE.md b/contributingGuides/STYLE.md index 6af3a82c2ff6..304811332916 100644 --- a/contributingGuides/STYLE.md +++ b/contributingGuides/STYLE.md @@ -50,6 +50,9 @@ - [Stateless components vs Pure Components vs Class based components vs Render Props](#stateless-components-vs-pure-components-vs-class-based-components-vs-render-props---when-to-use-what) - [Use Refs Appropriately](#use-refs-appropriately) - [Are we allowed to use [insert brand new React feature]?](#are-we-allowed-to-use-insert-brand-new-react-feature-why-or-why-not) +- [Handling Scroll Issues with Nested Lists in React Native](#handling-scroll-issues-with-nested-lists-in-react-native) + - [Wrong Approach (Using SelectionList)](#wrong-approach-using-selectionlist) + - [Correct Approach (Using SelectionList)](#correct-approach-using-selectionlist) - [React Hooks: Frequently Asked Questions](#react-hooks-frequently-asked-questions) - [Onyx Best Practices](#onyx-best-practices) - [Collection Keys](#collection-keys) @@ -1105,6 +1108,48 @@ There are several ways to use and declare refs and we prefer the [callback metho We love React and learning about all the new features that are regularly being added to the API. However, we try to keep our organization's usage of React limited to the most stable set of features that React offers. We do this mainly for **consistency** and so our engineers don't have to spend extra time trying to figure out how everything is working. That said, if you aren't sure if we have adopted something, please ask us first. + +## Handling Scroll Issues with Nested Lists in React Native + +### Problem + +When using `SelectionList` alongside other components (e.g., `Text`, `Button`), wrapping them inside a `ScrollView` can lead to alignment and performance issues. Additionally, using `ScrollView` with nested `FlatList` or `SectionList` causes the error: + +> "VirtualizedLists should never be nested inside plain ScrollViews with the same orientation." + +### Solution + +The correct approach is avoid using `ScrollView`. You can add props like `listHeaderComponent` and `listFooterComponent` to add other components before or after the list while keeping the layout scrollable. + +### Wrong Approach (Using `SelectionList`) + +```jsx + + Header Content + +