Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
- Support setting the Google Cloud Storage (GCS) test results bucket in `apptesting:execute` and `appdistribution:distribute`
- Added a `--wasm` flag to `firebase deploy` to compile Flutter Web to WebAssembly when building for Hosting
3 changes: 2 additions & 1 deletion src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ export const command = new Command("deploy")
"--dry-run",
"perform a dry run of your deployment. Validates your changes and builds your code without deploying any changes to your project. " +
"In order to provide better validation, this may still enable APIs on the target project",
);
)
.option("--wasm", "compile Flutter Web to WebAssembly when building the framework for Hosting");

if (experiments.isEnabled("apphostinglocalbuilds")) {
command.option(
Expand Down
24 changes: 24 additions & 0 deletions src/frameworks/flutter/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,30 @@ describe("Flutter", () => {
});
sinon.assert.calledWith(stub, "flutter", ["build", "web"], { cwd, stdio: "inherit" });
});

it("should pass --wasm when the wasm context is set", async () => {
const process = new EventEmitter() as any;
process.stdin = new Writable();
process.stdout = new EventEmitter();
process.stderr = new EventEmitter();
process.status = 0;

sandbox.stub(flutterUtils, "assertFlutterCliExists").returns(undefined);

const cwd = ".";

const stub = sandbox.stub(crossSpawn, "sync").returns(process as any);
Comment on lines +115 to +125

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

According to the Repository Style Guide (TypeScript section, line 38), we should avoid using any or unknown as an escape hatch. Additionally, crossSpawn.sync is synchronous and returns a SpawnSyncReturns object rather than an EventEmitter (which is returned by asynchronous spawn). Since the code under test only checks the status property, we can simplify the mock and type it properly using Partial and ReturnType to avoid any.

Suggested change
const process = new EventEmitter() as any;
process.stdin = new Writable();
process.stdout = new EventEmitter();
process.stderr = new EventEmitter();
process.status = 0;
sandbox.stub(flutterUtils, "assertFlutterCliExists").returns(undefined);
const cwd = ".";
const stub = sandbox.stub(crossSpawn, "sync").returns(process as any);
sandbox.stub(flutterUtils, "assertFlutterCliExists").returns(undefined);
const cwd = ".";
const mockResult: Partial<ReturnType<typeof crossSpawn.sync>> = { status: 0 };
const stub = sandbox.stub(crossSpawn, "sync").returns(mockResult as ReturnType<typeof crossSpawn.sync>);
References
  1. Never use any or unknown as an escape hatch. Define proper interfaces/types or use type guards. (link)


const result = build(cwd, undefined, { wasm: true });

expect(await result).to.deep.equal({
wantsBackend: false,
});
sinon.assert.calledWith(stub, "flutter", ["build", "web", "--wasm"], {
cwd,
stdio: "inherit",
});
});
});

describe("init", () => {
Expand Down
18 changes: 15 additions & 3 deletions src/frameworks/flutter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import { sync as spawnSync } from "cross-spawn";
import { copy, pathExists } from "fs-extra";
import { join } from "path";

import { BuildResult, Discovery, FrameworkType, SupportLevel } from "../interfaces";
import {
BuildResult,
Discovery,
FrameworkContext,
FrameworkType,
SupportLevel,
} from "../interfaces";
import { FirebaseError } from "../../error";
import { assertFlutterCliExists, getPubSpec, getAdditionalBuildArgs } from "./utils";
import { DART_RESERVED_WORDS, FALLBACK_PROJECT_NAME } from "./constants";
Expand Down Expand Up @@ -47,12 +53,18 @@ export function init(setup: any, config: any) {
return Promise.resolve();
}

export async function build(cwd: string): Promise<BuildResult> {
export async function build(
cwd: string,
_target?: string,
context?: FrameworkContext,
): Promise<BuildResult> {
assertFlutterCliExists();

const pubSpec = await getPubSpec(cwd);
const otherArgs = getAdditionalBuildArgs(pubSpec);
const buildArgs = ["build", "web", ...otherArgs].filter(Boolean);
// Compile to WebAssembly when `firebase deploy --wasm` is used.
const wasmArgs = context?.wasm ? ["--wasm"] : [];
const buildArgs = ["build", "web", ...otherArgs, ...wasmArgs].filter(Boolean);

const build = spawnSync("flutter", buildArgs, { cwd, stdio: "inherit" });
if (build.status !== 0) throw new FirebaseError("Unable to build your Flutter app");
Expand Down
1 change: 1 addition & 0 deletions src/frameworks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ export async function prepareFrameworks(
projectId: project,
site: options.site,
hostingChannel: context?.hostingChannel,
wasm: options.wasm,
};

let codegenFunctionsDirectory: Framework["ɵcodegenFunctionsDirectory"];
Expand Down
2 changes: 2 additions & 0 deletions src/frameworks/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@ export type FrameworksOptions = HostingOptions &
Options & {
frameworksDevModeHandle?: RequestHandler;
nonInteractive?: boolean;
wasm?: boolean;
};

export type FrameworkContext = {
projectId?: string;
hostingChannel?: string;
site?: string;
wasm?: boolean;
};

export interface Framework {
Expand Down