From af89315fd45eafdfe00d691adbec70b2c30446e6 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Tue, 9 May 2023 22:21:01 -0700 Subject: [PATCH] Fixup TypeScript with export maps (#1284) Summary: Pull Request resolved: https://github.com/facebook/yoga/pull/1284 This makes TypeScript resolution play nicely with export maps, and converts the entrypoints to TypeScript. We remove the non-export-map fallbacks as well, so the export maps are always followed. Tests are moved to load yoga from its external export, testing the entrypoints. This moves the only untyped bit to the binary wrapper, which another diff will move. Reviewed By: yungsters Differential Revision: D45713689 fbshipit-source-id: 228e6f2c9d53520230707a81e76c3c35fcd46de5 --- javascript/.eslintrc.js | 54 +++++++++---------- javascript/.gitignore | 1 + javascript/CMakeLists.txt | 2 +- javascript/README.md | 2 +- javascript/jest.setup.ts | 8 +-- javascript/just.config.ts | 33 ++++++++++-- javascript/package.json | 24 +++++---- javascript/src/entrypoint/_entryAsync.js | 15 ------ javascript/src/entrypoint/_entrySync.js | 11 ---- javascript/src/entrypoint/asmjs-async.js | 11 ---- javascript/src/entrypoint/asmjs-async.ts | 26 +++++++++ javascript/src/entrypoint/asmjs-sync.js | 11 ---- javascript/src/entrypoint/asmjs-sync.ts | 23 ++++++++ javascript/src/entrypoint/wasm-async.js | 11 ---- javascript/src/entrypoint/wasm-async.ts | 26 +++++++++ javascript/src/entrypoint/wasm-sync.js | 11 ---- javascript/src/entrypoint/wasm-sync.ts | 23 ++++++++ javascript/src/index.d.ts | 15 ------ javascript/src/index.js | 11 ---- javascript/src/sync.d.ts | 16 ------ javascript/src/sync.js | 11 ---- .../src/{wrapAsm.d.ts => wrapAssembly.d.ts} | 2 +- .../src/{wrapAsm.js => wrapAssembly.js} | 0 javascript/tests/bin/run-bench.ts | 7 +-- javascript/tsconfig.json | 5 +- 25 files changed, 183 insertions(+), 176 deletions(-) delete mode 100644 javascript/src/entrypoint/_entryAsync.js delete mode 100644 javascript/src/entrypoint/_entrySync.js delete mode 100644 javascript/src/entrypoint/asmjs-async.js create mode 100644 javascript/src/entrypoint/asmjs-async.ts delete mode 100644 javascript/src/entrypoint/asmjs-sync.js create mode 100644 javascript/src/entrypoint/asmjs-sync.ts delete mode 100644 javascript/src/entrypoint/wasm-async.js create mode 100644 javascript/src/entrypoint/wasm-async.ts delete mode 100644 javascript/src/entrypoint/wasm-sync.js create mode 100644 javascript/src/entrypoint/wasm-sync.ts delete mode 100644 javascript/src/index.d.ts delete mode 100644 javascript/src/index.js delete mode 100644 javascript/src/sync.d.ts delete mode 100644 javascript/src/sync.js rename javascript/src/{wrapAsm.d.ts => wrapAssembly.d.ts} (99%) rename javascript/src/{wrapAsm.js => wrapAssembly.js} (100%) diff --git a/javascript/.eslintrc.js b/javascript/.eslintrc.js index 2baa5f0fbe..a350868bcb 100644 --- a/javascript/.eslintrc.js +++ b/javascript/.eslintrc.js @@ -7,20 +7,20 @@ * @format */ -const path = require("path"); +const path = require('path'); module.exports = { root: true, - ignorePatterns: ["dist/**", "tests/generated/**"], - extends: ["eslint:recommended", "plugin:prettier/recommended"], - plugins: ["prettier"], + ignorePatterns: ['dist/**', 'tests/generated/**'], + extends: ['eslint:recommended', 'plugin:prettier/recommended'], + plugins: ['prettier'], rules: { - "no-var": "error", - "prefer-arrow-callback": "error", - "prefer-const": "error", - "prefer-object-spread": "error", - "prefer-spread": "error", - "require-await": "error", + 'no-var': 'error', + 'prefer-arrow-callback': 'error', + 'prefer-const': 'error', + 'prefer-object-spread': 'error', + 'prefer-spread': 'error', + 'require-await': 'error', }, env: { commonjs: true, @@ -28,44 +28,44 @@ module.exports = { }, overrides: [ { - files: ["**/*.js"], - parser: "@babel/eslint-parser", + files: ['**/*.js'], + parser: '@babel/eslint-parser', parserOptions: { babelOptions: { - configFile: path.join(__dirname, ".babelrc.js"), + configFile: path.join(__dirname, '.babelrc.js'), }, }, }, { - files: ["**/*.ts"], - extends: ["plugin:@typescript-eslint/recommended"], - parser: "@typescript-eslint/parser", + files: ['**/*.ts'], + extends: ['plugin:@typescript-eslint/recommended'], + parser: '@typescript-eslint/parser', parserOptions: { - project: path.join(__dirname, "tsconfig.json"), + project: path.join(__dirname, 'tsconfig.json'), }, - plugins: ["@typescript-eslint"], + plugins: ['@typescript-eslint'], rules: { - "@typescript-eslint/no-var-requires": "off", + '@typescript-eslint/no-var-requires': 'off', }, }, { - files: ["**/.eslintrc.js", "**/just.config.js"], + files: ['**/.eslintrc.js', '**/just.config.js'], env: { node: true, }, }, { - files: ["jest.*", "tests/**"], + files: ['jest.*', 'tests/**'], env: { node: true, }, - extends: ["plugin:jest/recommended"], + extends: ['plugin:jest/recommended'], globals: { - getMeasureCounter: "writable", - getMeasureCounterMax: "writable", - getMeasureCounterMin: "writable", - Yoga: "writable", - YGBENCHMARK: "writable", + getMeasureCounter: 'writable', + getMeasureCounterMax: 'writable', + getMeasureCounterMin: 'writable', + Yoga: 'writable', + YGBENCHMARK: 'writable', }, }, ], diff --git a/javascript/.gitignore b/javascript/.gitignore index 29751e5c70..198af7f76a 100644 --- a/javascript/.gitignore +++ b/javascript/.gitignore @@ -1,3 +1,4 @@ +/binaries /build /dist /node_modules diff --git a/javascript/CMakeLists.txt b/javascript/CMakeLists.txt index 01177fcc21..afb54bb551 100644 --- a/javascript/CMakeLists.txt +++ b/javascript/CMakeLists.txt @@ -49,7 +49,7 @@ link_libraries(embind) add_library(yogaObjLib OBJECT ${SOURCES}) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/dist/build) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/binaries) add_executable(asmjs-sync $) target_link_options(asmjs-sync PUBLIC diff --git a/javascript/README.md b/javascript/README.md index ceeb50ac88..87b07a2ef3 100644 --- a/javascript/README.md +++ b/javascript/README.md @@ -44,7 +44,7 @@ For better performance and smaller packages, WebAssembly is preferred to asm.js A specific entrypoint may be specified on platforms which do not understand export conditions. ```ts -import {loadYoga} from 'yoga-layout/dist/entrypoint/wasm-async'; +import {loadYoga} from 'yoga-layout/wasm-async'; ``` diff --git a/javascript/jest.setup.ts b/javascript/jest.setup.ts index 1363981762..7ec11a771c 100644 --- a/javascript/jest.setup.ts +++ b/javascript/jest.setup.ts @@ -9,13 +9,13 @@ module.exports = async () => { if (process.env['SYNC'] === '1' && process.env['WASM'] === '1') { - globalThis.Yoga = require('./dist/entrypoint/wasm-sync'); + globalThis.Yoga = require('yoga-layout/wasm-sync').default; } else if (process.env['SYNC'] === '1') { - globalThis.Yoga = require('./dist/entrypoint/asmjs-sync'); + globalThis.Yoga = require('yoga-layout/asmjs-sync').default; } else if (process.env['WASM'] === '1') { - globalThis.Yoga = await require('./dist/entrypoint/wasm-async').loadYoga(); + globalThis.Yoga = await require('yoga-layout/wasm-async').loadYoga(); } else { - globalThis.Yoga = await require('./dist/entrypoint/asmjs-async').loadYoga(); + globalThis.Yoga = await require('yoga-layout/asmjs-async').loadYoga(); } }; diff --git a/javascript/just.config.ts b/javascript/just.config.ts index dcc7776e6b..11d45988e8 100644 --- a/javascript/just.config.ts +++ b/javascript/just.config.ts @@ -21,6 +21,8 @@ import { tscTask, } from 'just-scripts'; +import {readFile, writeFile} from 'fs/promises'; + import glob from 'glob'; import path from 'path'; import which from 'which'; @@ -84,15 +86,38 @@ task( ), ); +task('prepack-package-json', async () => { + const packageJsonPath = path.join(__dirname, 'package.json'); + const packageJsonContents = await readFile(packageJsonPath); + const packageJson = JSON.parse(packageJsonContents.toString('utf-8')); + + recursiveReplace(packageJson, /(.\/src\/.*)\.ts/, '$1.js'); + await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2)); +}); + task( - 'prepublish', - parallel( - 'build', - tscTask({emitDeclarationOnly: true}), + 'prepack', + series( + parallel('build', tscTask({emitDeclarationOnly: true})), babelTransformTask({dir: 'src'}), + 'prepack-package-json', ), ); +function recursiveReplace( + obj: Record, + pattern: RegExp, + replacement: string, +) { + for (const [key, value] of Object.entries(obj)) { + if (typeof value === 'string') { + obj[key] = value.replace(pattern, replacement); + } else if (typeof value === 'object' && value != null) { + recursiveReplace(value as Record, pattern, replacement); + } + } +} + function babelTransformTask(opts: {dir: string}) { return () => { const args = [ diff --git a/javascript/package.json b/javascript/package.json index 683ec3b317..b1f9d91362 100644 --- a/javascript/package.json +++ b/javascript/package.json @@ -7,22 +7,24 @@ "type": "git", "url": "git@github.com:facebook/yoga.git" }, - "main": "./src/index.js", - "types": "./src/index.d.ts", "exports": { ".": { - "browser": "./src/entrypoint/wasm-async.js", - "node": "./src/entrypoint/wasm-async.js", - "default": "./src/entrypoint/asmjs-async.js" + "browser": "./src/entrypoint/wasm-async.ts", + "node": "./src/entrypoint/wasm-async.ts", + "default": "./src/entrypoint/asmjs-async.ts" }, "./sync": { - "browser": "./src/entrypoint/asmjs-sync.js", - "node": "./src/entrypoint/wasm-sync.js", - "default": "./src/entrypoint/asmjs-sync.js" - } + "browser": "./src/entrypoint/asmjs-sync.ts", + "node": "./src/entrypoint/wasm-sync.ts", + "default": "./src/entrypoint/asmjs-sync.ts" + }, + "./asmjs-async": "./src/entrypoint/asmjs-async.ts", + "./asmjs-sync": "./src/entrypoint/asmjs-sync.ts", + "./wasm-async": "./src/entrypoint/wasm-async.ts", + "./wasm-sync": "./src/entrypoint/wasm-sync.ts" }, "files": [ - "dist/**", + "binaries/**", "src/**" ], "scripts": { @@ -31,7 +33,7 @@ "clean": "just clean", "lint": "just lint", "lint:fix": "just lint --fix", - "prepublish": "just prepublish", + "prepack": "just prepack", "test": "just test", "test:asmjs-async": "just test:asmjs-async", "test:asmjs-sync": "just test:asmjs-sync", diff --git a/javascript/src/entrypoint/_entryAsync.js b/javascript/src/entrypoint/_entryAsync.js deleted file mode 100644 index 7d46c388b5..0000000000 --- a/javascript/src/entrypoint/_entryAsync.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 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. - * - * @format - */ - -const wrapAsm = require('../wrapAsm'); - -module.exports = loadAsm => ({ - loadYoga: () => loadAsm().then(wrapAsm), - ...require('../generated/YGEnums'), -}); diff --git a/javascript/src/entrypoint/_entrySync.js b/javascript/src/entrypoint/_entrySync.js deleted file mode 100644 index 9495d98cb6..0000000000 --- a/javascript/src/entrypoint/_entrySync.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * 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. - * - * @format - */ - -const wrapAsm = require('../wrapAsm'); -module.exports = asm => wrapAsm(asm()); diff --git a/javascript/src/entrypoint/asmjs-async.js b/javascript/src/entrypoint/asmjs-async.js deleted file mode 100644 index eb1b52d77f..0000000000 --- a/javascript/src/entrypoint/asmjs-async.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * 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. - * - * @format - */ - -const asm = require('../build/asmjs-async'); -module.exports = require('./_entryAsync')(asm); diff --git a/javascript/src/entrypoint/asmjs-async.ts b/javascript/src/entrypoint/asmjs-async.ts new file mode 100644 index 0000000000..5ac7c71a68 --- /dev/null +++ b/javascript/src/entrypoint/asmjs-async.ts @@ -0,0 +1,26 @@ +/** + * 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. + * + * @format + */ + +import wrapAssembly from '../wrapAssembly'; +import type {Yoga} from '../wrapAssembly'; + +export * from '../generated/YGEnums'; +export type { + Config, + DirtiedFunction, + MeasureFunction, + Node, + Yoga, +} from '../wrapAssembly'; + +const loadAssembly = require('../../binaries/asmjs-async'); + +export async function loadYoga(): Promise { + return wrapAssembly(await loadAssembly()); +} diff --git a/javascript/src/entrypoint/asmjs-sync.js b/javascript/src/entrypoint/asmjs-sync.js deleted file mode 100644 index 6919771408..0000000000 --- a/javascript/src/entrypoint/asmjs-sync.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * 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. - * - * @format - */ - -const asm = require('../build/asmjs-sync'); -module.exports = require('./_entrySync')(asm); diff --git a/javascript/src/entrypoint/asmjs-sync.ts b/javascript/src/entrypoint/asmjs-sync.ts new file mode 100644 index 0000000000..710dbdbe75 --- /dev/null +++ b/javascript/src/entrypoint/asmjs-sync.ts @@ -0,0 +1,23 @@ +/** + * 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. + * + * @format + */ + +import wrapAssembly from '../wrapAssembly'; + +export * from '../generated/YGEnums'; +export type { + Config, + DirtiedFunction, + MeasureFunction, + Node, + Yoga, +} from '../wrapAssembly'; + +const loadAssembly = require('../../binaries/asmjs-sync'); +const Yoga = wrapAssembly(loadAssembly()); +export default Yoga; diff --git a/javascript/src/entrypoint/wasm-async.js b/javascript/src/entrypoint/wasm-async.js deleted file mode 100644 index ad5be4216e..0000000000 --- a/javascript/src/entrypoint/wasm-async.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * 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. - * - * @format - */ - -const asm = require('../build/wasm-async'); -module.exports = require('./_entryAsync')(asm); diff --git a/javascript/src/entrypoint/wasm-async.ts b/javascript/src/entrypoint/wasm-async.ts new file mode 100644 index 0000000000..752f759da1 --- /dev/null +++ b/javascript/src/entrypoint/wasm-async.ts @@ -0,0 +1,26 @@ +/** + * 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. + * + * @format + */ + +import wrapAssembly from '../wrapAssembly'; +import type {Yoga} from '../wrapAssembly'; + +export * from '../generated/YGEnums'; +export type { + Config, + DirtiedFunction, + MeasureFunction, + Node, + Yoga, +} from '../wrapAssembly'; + +const loadAssembly = require('../../binaries/wasm-async'); + +export async function loadYoga(): Promise { + return wrapAssembly(await loadAssembly()); +} diff --git a/javascript/src/entrypoint/wasm-sync.js b/javascript/src/entrypoint/wasm-sync.js deleted file mode 100644 index 88983508a1..0000000000 --- a/javascript/src/entrypoint/wasm-sync.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * 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. - * - * @format - */ - -const asm = require('../build/wasm-sync'); -module.exports = require('./_entrySync')(asm); diff --git a/javascript/src/entrypoint/wasm-sync.ts b/javascript/src/entrypoint/wasm-sync.ts new file mode 100644 index 0000000000..ff9bf7026f --- /dev/null +++ b/javascript/src/entrypoint/wasm-sync.ts @@ -0,0 +1,23 @@ +/** + * 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. + * + * @format + */ + +import wrapAssembly from '../wrapAssembly'; + +export * from '../generated/YGEnums'; +export type { + Config, + DirtiedFunction, + MeasureFunction, + Node, + Yoga, +} from '../wrapAssembly'; + +const loadAssembly = require('../../binaries/wasm-sync'); +const Yoga = wrapAssembly(loadAssembly()); +export default Yoga; diff --git a/javascript/src/index.d.ts b/javascript/src/index.d.ts deleted file mode 100644 index fb2830e4c1..0000000000 --- a/javascript/src/index.d.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * 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. - * - * @format - */ - -import type {Yoga} from './wrapAsm'; - -export * from './generated/YGEnums'; -export * from './wrapAsm'; - -export function loadYoga(): Promise; diff --git a/javascript/src/index.js b/javascript/src/index.js deleted file mode 100644 index 06a348c46b..0000000000 --- a/javascript/src/index.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * 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. - * - * @format - */ - -// Fallback for when the export map is not followed -module.exports = require('./entrypoint/asmjs-async'); diff --git a/javascript/src/sync.d.ts b/javascript/src/sync.d.ts deleted file mode 100644 index 27bd8ecc29..0000000000 --- a/javascript/src/sync.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * 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. - * - * @format - */ - -import type {Yoga} from './wrapAsm'; - -export * from './generated/YGEnums'; -export * from './wrapAsm'; - -declare const yoga: Yoga; -export default yoga; diff --git a/javascript/src/sync.js b/javascript/src/sync.js deleted file mode 100644 index 6ac610efe7..0000000000 --- a/javascript/src/sync.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * 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. - * - * @format - */ - -// Fallback for when the export map is not followed -module.exports = require('./entrypoint/asmjs-sync'); diff --git a/javascript/src/wrapAsm.d.ts b/javascript/src/wrapAssembly.d.ts similarity index 99% rename from javascript/src/wrapAsm.d.ts rename to javascript/src/wrapAssembly.d.ts index d3dc069d28..0b0b38b241 100644 --- a/javascript/src/wrapAsm.d.ts +++ b/javascript/src/wrapAssembly.d.ts @@ -187,5 +187,5 @@ export type Yoga = { }; } & typeof YGEnums; -declare const wrapAsm: () => Yoga; +declare const wrapAsm: (assembly: unknown) => Yoga; export default wrapAsm; diff --git a/javascript/src/wrapAsm.js b/javascript/src/wrapAssembly.js similarity index 100% rename from javascript/src/wrapAsm.js rename to javascript/src/wrapAssembly.js diff --git a/javascript/tests/bin/run-bench.ts b/javascript/tests/bin/run-bench.ts index 836f7f0b75..d5c4d0883d 100644 --- a/javascript/tests/bin/run-bench.ts +++ b/javascript/tests/bin/run-bench.ts @@ -10,6 +10,9 @@ import path from 'path'; +import YogaAsmjs from 'yoga-layout/asmjs-sync'; +import YogaWasm from 'yoga-layout/wasm-sync'; + const WARMUP_ITERATIONS = 3; const BENCHMARK_ITERATIONS = 10; @@ -18,9 +21,7 @@ const testFiles = process.argv.slice(2); const testResults = new Map>(); for (const type of ['asmjs', 'wasm']) { - globalThis.Yoga = require(type === 'asmjs' - ? '../../dist/entrypoint/asmjs-sync' - : '../../dist/entrypoint/wasm-sync'); + globalThis.Yoga = type === 'asmjs' ? YogaAsmjs : YogaWasm; for (const file of testFiles) { globalThis.YGBENCHMARK = (name: string, fn: () => void) => { diff --git a/javascript/tsconfig.json b/javascript/tsconfig.json index 2568d1e1d2..ba6074ad48 100644 --- a/javascript/tsconfig.json +++ b/javascript/tsconfig.json @@ -6,8 +6,11 @@ "declaration": true, "esModuleInterop": true, "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, + // TODO: moduleResolution: "nodenext" is buggy with this if the absolute + // path contains any capital letters + "forceConsistentCasingInFileNames": false, "baseUrl": ".", + "moduleResolution": "nodenext", "paths": { "yoga-layout": ["src"] }