#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

# Drives the hermes-parser contract test suite (ported under
# flow/packages/flow-parser/__tests__/) against the live wasm-built
# Flow parser. Same wasm-bootstrap dance as runWasmFixtures.sh: build the wasm
# under emcc with --isolation-dir, drop it into oxidized-src/FlowParserWASM.js,
# then invoke jest filtered to flow-parser only via the unified workspace
# jest config at flow/packages/jest.config.js.

set -e -o pipefail

FBCODE_DIR=${1:?"missing FBCODE_DIR arg"}
# Resolve everything to absolute paths BEFORE any `cd`, since FBCODE_DIR may
# be `.` and we cd into the workspace root before opening the dist flock.
FBCODE_ABS=$(cd "$FBCODE_DIR" && pwd -P)

WORKSPACE_DIR="$FBCODE_ABS/flow/packages"
PKG_DIR="$WORKSPACE_DIR/flow-parser"
PKG_SRC="$PKG_DIR/oxidized-src"
WASM_OUT="$PKG_SRC/FlowParserWASM.js"

WASM_PATH=$(buck2 --isolation-dir flow_contract_test build \
    @fbsource//xplat/mode/hermes/opt \
    @fbsource//xplat/mode/emcc \
    fbcode//flow/packages/flow-parser-wasm:flow-parser-wasm.js \
    --show-full-output 2>&1 \
    | awk '/flow-parser-wasm\.js$/ {print $2; exit}')

if [ -z "$WASM_PATH" ] || [ ! -f "$WASM_PATH" ]; then
    echo "Failed to build flow-parser-wasm.js (got '$WASM_PATH')" >&2
    exit 1
fi

# PID-counted cleanup — `runWasmFixtures.sh` and this script both write to the
# same `$WASM_OUT` (the JS test harness `require()`s a hard-coded
# `./FlowParserWASM` path so we can't trivially split the artifacts). When buck
# schedules both targets in parallel, a naive `trap rm -f` from one would
# delete the file mid-run for the other. Instead, each script registers its
# PID under a shared lock dir; on exit each removes its own PID and only the
# last one out (`rmdir` succeeds == dir is empty) deletes `$WASM_OUT`.
WASM_LOCK_DIR="${WASM_OUT}.lock.d"
mkdir -p "$WASM_LOCK_DIR"
WASM_MARKER="$WASM_LOCK_DIR/$$"
mkdir "$WASM_MARKER"
trap '
    JEST_EXIT_CODE=$?
    rmdir "$WASM_MARKER" 2>/dev/null || true
    if rmdir "$WASM_LOCK_DIR" 2>/dev/null; then
        rm -f "$WASM_OUT" || true
    fi
    exit "$JEST_EXIT_CODE"
' EXIT

{
    # Same MIT header preamble as runWasmFixtures.sh — needed because the
    # wasm artifact ships without one and license-lint expects every JS file
    # in src/ to have it. Built echo-by-echo so license-lint doesn't false-
    # positive on a duplicate of this file's own header.
    echo '/**'
    echo ' * Copyright (c) Meta Platforms, Inc. and affiliates.'
    echo ' *'
    echo ' * This source code is licensed under the MIT license found in the'
    echo ' * LICENSE file in the root directory of this source tree.'
    echo ' */'
    echo ''
    echo "'use strict';"
    cat "$WASM_PATH"
} > "$WASM_OUT"

cd "$WORKSPACE_DIR"

# Serialize dist/ rebuilds + reads against runOxidizedJestTests.sh, which
# rebuilds the generated parser output and then `require()`s
# `flow-parser`. Buck
# schedules `wasm_parser_contract_test` and `oxidized_jest_test` in
# parallel; without this flock, target A's jest can read a torn
# `index.js` (un-stripped Flow `import type` lines from the copy
# step) while target B's `yarn build` is mid-flight. Hold the lock through
# both `yarn build` AND the jest run so a concurrent target can't clobber
# generated parser output while we're reading from it.
DIST_FLOCK="$PKG_DIR/.dist.flock"
exec 9> "$DIST_FLOCK"
echo "==> waiting for exclusive lock on $DIST_FLOCK"
flock -x 9
echo "==> acquired lock on $DIST_FLOCK"

# yarn.lock is regenerated on first install. Subsequent runs reuse the locked
# tree. We rely on the global fbcode yarn offline mirror so --offline is the
# right mode here.
yarn install --offline

# Build dist/ output from oxidized-src/: contract tests `require()` the
# public entry `flow-parser`. Without this build, jest sees the ESM
# `oxidized-src/` files directly and node can't `require()` them.
yarn build

# Use the fbsource third-party Node toolchain (24.x). The system Node may be
# 16.x which lacks `os.availableParallelism()` — required by jest 30's
# `getMaxWorkers`.
#
# Filter to flow-parser only — the unified jest config picks up
# every package's __tests__/ but contract tests scope to the parser surface.
"$FBCODE_ABS/../xplat/third-party/node/bin/node" --experimental-vm-modules \
    ./node_modules/.bin/jest --no-coverage \
    --testPathPatterns='flow-parser'
