From dac7f34e927ffe316f933f91869afb3ead3418f1 Mon Sep 17 00:00:00 2001 From: Abram Sanderson Date: Fri, 16 Jun 2023 09:25:10 -0700 Subject: [PATCH] Convert webflow example to use esnext modules (#15940) ## Description This makes progress toward #15917 by converting @fluid-example/webflow to use esnext modules. This is the most involved update of any of the packages that depend on `@fluid-internal/test-version-utils`, since ESNext modules don't support the `require.extensions` api (they don't support `require`), which was the technique used to make webpack-style CSS imports not blow up when run in a node environment. Instead, it's recommended to use the node esm loader API. Although this API is experimental, it hasn't receive any changes between v16.12 and the node latest (v20.x). --- .../webflow/{.eslintrc.js => .eslintrc.cjs} | 6 +++++ examples/data-objects/webflow/package.json | 26 +++++++++---------- .../webflow/src/clipboard/debug.ts | 2 +- .../webflow/src/clipboard/paste.ts | 6 ++--- .../webflow/src/document/debug.ts | 2 +- .../webflow/src/document/index.ts | 10 +++---- .../data-objects/webflow/src/editor/caret.ts | 10 +++---- .../data-objects/webflow/src/editor/debug.ts | 2 +- .../data-objects/webflow/src/editor/editor.ts | 14 +++++----- .../data-objects/webflow/src/editor/index.ts | 2 +- .../data-objects/webflow/src/host/debug.ts | 2 +- .../data-objects/webflow/src/host/index.ts | 4 +-- .../webflow/src/host/searchmenu/index.ts | 6 ++--- .../data-objects/webflow/src/host/webFlow.ts | 4 +-- .../webflow/src/host/webflowView.tsx | 14 +++++----- examples/data-objects/webflow/src/html/css.ts | 4 +-- .../data-objects/webflow/src/html/debug.ts | 2 +- .../webflow/src/html/formatters.ts | 16 ++++++------ examples/data-objects/webflow/src/index.ts | 8 +++--- examples/data-objects/webflow/src/package.ts | 7 ++--- .../webflow/src/packageVersion.ts | 9 +++++++ .../src/test/{.mocharc.js => .mocharc.cjs} | 2 +- .../webflow/src/test/direction.spec.ts | 2 +- .../webflow/src/test/flowdocument.spec.ts | 4 +-- .../webflow/src/test/layout.spec.ts | 10 +++---- .../webflow/src/test/segmentspan.spec.ts | 4 +-- .../webflow/src/test/strings.spec.ts | 2 +- .../webflow/src/test/tokenlist.spec.ts | 2 +- .../webflow/src/test/tsconfig.json | 1 + .../data-objects/webflow/src/util/attr.ts | 4 +-- .../data-objects/webflow/src/util/caret.ts | 4 +-- .../data-objects/webflow/src/util/index.ts | 22 ++++++++-------- .../data-objects/webflow/src/util/localref.ts | 4 +-- examples/data-objects/webflow/src/util/tag.ts | 4 +-- .../webflow/src/util/tokenlist.ts | 2 +- .../data-objects/webflow/src/view/debug.ts | 2 +- .../webflow/src/view/formatter.ts | 4 +-- .../data-objects/webflow/src/view/layout.ts | 10 +++---- .../data-objects/webflow/tsconfig.esnext.json | 7 ----- examples/data-objects/webflow/tsconfig.json | 6 +++++ .../{webpack.config.js => webpack.config.cjs} | 8 +++++- .../{webpack.dev.js => webpack.dev.cjs} | 0 .../{webpack.prod.js => webpack.prod.cjs} | 0 .../test-version-utils/src/versionUtils.ts | 15 ++++++++--- pnpm-lock.yaml | 25 +++++++++++++----- 45 files changed, 169 insertions(+), 131 deletions(-) rename examples/data-objects/webflow/{.eslintrc.js => .eslintrc.cjs} (56%) create mode 100644 examples/data-objects/webflow/src/packageVersion.ts rename examples/data-objects/webflow/src/test/{.mocharc.js => .mocharc.cjs} (80%) delete mode 100644 examples/data-objects/webflow/tsconfig.esnext.json rename examples/data-objects/webflow/{webpack.config.js => webpack.config.cjs} (84%) rename examples/data-objects/webflow/{webpack.dev.js => webpack.dev.cjs} (100%) rename examples/data-objects/webflow/{webpack.prod.js => webpack.prod.cjs} (100%) diff --git a/examples/data-objects/webflow/.eslintrc.js b/examples/data-objects/webflow/.eslintrc.cjs similarity index 56% rename from examples/data-objects/webflow/.eslintrc.js rename to examples/data-objects/webflow/.eslintrc.cjs index 3a5a22d5ad7c..7ab6fdb650b1 100644 --- a/examples/data-objects/webflow/.eslintrc.js +++ b/examples/data-objects/webflow/.eslintrc.cjs @@ -12,6 +12,12 @@ module.exports = { "max-len": "off", "no-bitwise": "off", "no-case-declarations": "off", + // ESLint's resolver doesn't resolve relative imports of ESNext modules correctly, since + // it resolves the path relative to the .ts file (and assumes a file with a .js extension + // should exist there) + // AB#4614 tracks moving to eslint-import-resolver-typescript (which handles such imports + // out of the box) and removing this exception. + "import/no-unresolved": ["error", { ignore: ["^\\.(.*)\\.js$"] }], }, parserOptions: { project: ["./tsconfig.json", "./src/test/tsconfig.json"], diff --git a/examples/data-objects/webflow/package.json b/examples/data-objects/webflow/package.json index 62f240ff7a17..580f9a64c4ba 100644 --- a/examples/data-objects/webflow/package.json +++ b/examples/data-objects/webflow/package.json @@ -12,15 +12,15 @@ "license": "MIT", "author": "Microsoft and contributors", "sideEffects": false, + "type": "module", "main": "dist/index.js", - "module": "lib/index.js", + "module": "dist/index.js", "types": "dist/index.d.ts", "scripts": { "build": "fluid-build . --task build", - "build:commonjs": "fluid-build . --task commonjs", "build:compile": "fluid-build . --task compile", - "build:copy": "copyfiles -u 1 \"src/**/*.css\" dist/ && copyfiles -u 1 \"src/**/*.css\" lib/", - "build:esnext": "tsc --project ./tsconfig.esnext.json", + "build:copy": "copyfiles -u 1 \"src/**/*.css\" dist/", + "build:esnext": "tsc", "build:test": "tsc --project ./src/test/tsconfig.json", "clean": "rimraf dist lib *.tsbuildinfo *.build.log", "dev": "npm run build:esnext -- --watch", @@ -32,16 +32,16 @@ "prepack": "npm run webpack", "prettier": "prettier --check . --ignore-path ../../../.prettierignore", "prettier:fix": "prettier --write . --ignore-path ../../../.prettierignore", - "start": "webpack serve --config webpack.config.js", - "start:docker": "webpack serve --config webpack.config.js --env mode=docker", - "start:r11s": "webpack serve --config webpack.config.js --env mode=r11s", - "start:single": "webpack serve --config webpack.config.js --env.single true", - "start:spo": "webpack serve --config webpack.config.js --env mode=spo", - "start:spo-df": "webpack serve --config webpack.config.js --env mode=spo-df", - "start:tinylicious": "webpack serve --config webpack.config.js --env mode=tinylicious", + "start": "webpack serve --config webpack.config.cjs", + "start:docker": "webpack serve --config webpack.config.cjs --env mode=docker", + "start:r11s": "webpack serve --config webpack.config.cjs --env mode=r11s", + "start:single": "webpack serve --config webpack.config.cjs --env.single true", + "start:spo": "webpack serve --config webpack.config.cjs --env mode=spo", + "start:spo-df": "webpack serve --config webpack.config.cjs --env mode=spo-df", + "start:tinylicious": "webpack serve --config webpack.config.cjs --env mode=tinylicious", "test": "npm run test:mocha", "test:coverage": "nyc npm test -- --reporter xunit --reporter-option output=nyc/junit-report.xml --exit", - "test:mocha": "mocha --config src/test/.mocharc.js dist/test/**/*.spec.js", + "test:mocha": "cross-env NODE_OPTIONS=\"--experimental-loader esm-loader-css\" mocha --config src/test/.mocharc.cjs dist/test/**/*.spec.js", "test:mocha:multireport": "cross-env FLUID_TEST_MULTIREPORT=1 npm run test:mocha", "test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha", "tsc": "tsc", @@ -109,9 +109,9 @@ "cross-env": "^7.0.3", "css-loader": "^1.0.0", "eslint": "~8.6.0", + "esm-loader-css": "^1.0.4", "file-loader": "^3.0.1", "html-loader": "^3.1.0", - "ignore-styles": "^5.0.1", "jsdom": "^16.7.0", "jsdom-global": "^3.0.2", "mocha": "^10.2.0", diff --git a/examples/data-objects/webflow/src/clipboard/debug.ts b/examples/data-objects/webflow/src/clipboard/debug.ts index f448f252da6d..b5e8d54f622c 100644 --- a/examples/data-objects/webflow/src/clipboard/debug.ts +++ b/examples/data-objects/webflow/src/clipboard/debug.ts @@ -4,6 +4,6 @@ */ import { IDebugger } from "debug"; -import { debug as parent } from "../debug"; +import { debug as parent } from "../debug.js"; export const debug: IDebugger = parent.extend("clipboard"); diff --git a/examples/data-objects/webflow/src/clipboard/paste.ts b/examples/data-objects/webflow/src/clipboard/paste.ts index dc2f2c62c378..3df400e23418 100644 --- a/examples/data-objects/webflow/src/clipboard/paste.ts +++ b/examples/data-objects/webflow/src/clipboard/paste.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. */ -import { FlowDocument } from "../document"; -import { TagName } from "../util"; -import { debug } from "./debug"; +import { FlowDocument } from "../document/index.js"; +import { TagName } from "../util/index.js"; +import { debug } from "./debug.js"; const enum ClipboardFormat { html = "text/html", diff --git a/examples/data-objects/webflow/src/document/debug.ts b/examples/data-objects/webflow/src/document/debug.ts index 951bb28721f8..9db8df23ec35 100644 --- a/examples/data-objects/webflow/src/document/debug.ts +++ b/examples/data-objects/webflow/src/document/debug.ts @@ -4,6 +4,6 @@ */ import { IDebugger } from "debug"; -import { debug as parent } from "../debug"; +import { debug as parent } from "../debug.js"; export const debug: IDebugger = parent.extend("document"); diff --git a/examples/data-objects/webflow/src/document/index.ts b/examples/data-objects/webflow/src/document/index.ts index 897fed28ab26..c3a2a7ecbbe1 100644 --- a/examples/data-objects/webflow/src/document/index.ts +++ b/examples/data-objects/webflow/src/document/index.ts @@ -41,11 +41,11 @@ import { } from "@fluidframework/sequence"; import { ISharedDirectory, SharedDirectory } from "@fluidframework/map"; import { IEvent } from "@fluidframework/common-definitions"; -import { clamp, emptyArray, randomId, TagName, TokenList } from "../util"; -import { IHTMLAttributes } from "../util/attr"; -import { documentType } from "../package"; -import { debug } from "./debug"; -import { SegmentSpan } from "./segmentspan"; +import { clamp, emptyArray, randomId, TagName, TokenList } from "../util/index.js"; +import { IHTMLAttributes } from "../util/attr.js"; +import { documentType } from "../package.js"; +import { debug } from "./debug.js"; +import { SegmentSpan } from "./segmentspan.js"; export const enum DocSegmentKind { text = "text", diff --git a/examples/data-objects/webflow/src/editor/caret.ts b/examples/data-objects/webflow/src/editor/caret.ts index 2c99ea2893e8..eddfa9ad6243 100644 --- a/examples/data-objects/webflow/src/editor/caret.ts +++ b/examples/data-objects/webflow/src/editor/caret.ts @@ -4,12 +4,12 @@ */ import { LocalReferencePosition, ReferencePosition } from "@fluidframework/merge-tree"; -import { DocSegmentKind, getDocSegmentKind } from "../document"; -import { clamp, Dom, hasTagName, TagName } from "../util"; -import { updateRef } from "../util/localref"; +import { DocSegmentKind, getDocSegmentKind } from "../document/index.js"; +import { clamp, Dom, hasTagName, TagName } from "../util/index.js"; +import { updateRef } from "../util/localref.js"; -import { eotSegment, Layout } from "../view/layout"; -import { debug } from "./debug"; +import { eotSegment, Layout } from "../view/layout.js"; +import { debug } from "./debug.js"; export class Caret { private get doc() { diff --git a/examples/data-objects/webflow/src/editor/debug.ts b/examples/data-objects/webflow/src/editor/debug.ts index 9609645704ea..3630b0af117e 100644 --- a/examples/data-objects/webflow/src/editor/debug.ts +++ b/examples/data-objects/webflow/src/editor/debug.ts @@ -4,6 +4,6 @@ */ import { IDebugger } from "debug"; -import { debug as parent } from "../debug"; +import { debug as parent } from "../debug.js"; export const debug: IDebugger = parent.extend("editor"); diff --git a/examples/data-objects/webflow/src/editor/editor.ts b/examples/data-objects/webflow/src/editor/editor.ts index 2c50f0f66d7b..30c94b8932e9 100644 --- a/examples/data-objects/webflow/src/editor/editor.ts +++ b/examples/data-objects/webflow/src/editor/editor.ts @@ -4,13 +4,13 @@ */ import { FluidObject } from "@fluidframework/core-interfaces"; -import { paste } from "../clipboard/paste"; -import { FlowDocument } from "../document"; -import { Direction, getDeltaX, KeyCode } from "../util"; -import { IFormatterState, RootFormatter } from "../view/formatter"; -import { Layout } from "../view/layout"; -import { Caret } from "./caret"; -import { debug } from "./debug"; +import { paste } from "../clipboard/paste.js"; +import { FlowDocument } from "../document/index.js"; +import { Direction, getDeltaX, KeyCode } from "../util/index.js"; +import { IFormatterState, RootFormatter } from "../view/formatter.js"; +import { Layout } from "../view/layout.js"; +import { Caret } from "./caret.js"; +import { debug } from "./debug.js"; export class Editor { private readonly layout: Layout; diff --git a/examples/data-objects/webflow/src/editor/index.ts b/examples/data-objects/webflow/src/editor/index.ts index f61ec26030bc..1f3748d77b1d 100644 --- a/examples/data-objects/webflow/src/editor/index.ts +++ b/examples/data-objects/webflow/src/editor/index.ts @@ -3,4 +3,4 @@ * Licensed under the MIT License. */ -export { Editor } from "./editor"; +export { Editor } from "./editor.js"; diff --git a/examples/data-objects/webflow/src/host/debug.ts b/examples/data-objects/webflow/src/host/debug.ts index 18c86fbd19bd..59c070dbf325 100644 --- a/examples/data-objects/webflow/src/host/debug.ts +++ b/examples/data-objects/webflow/src/host/debug.ts @@ -4,6 +4,6 @@ */ import { IDebugger } from "debug"; -import { debug as parent } from "../debug"; +import { debug as parent } from "../debug.js"; export const debug: IDebugger = parent.extend("host"); diff --git a/examples/data-objects/webflow/src/host/index.ts b/examples/data-objects/webflow/src/host/index.ts index 53ed7a02f113..ecd73ec1b322 100644 --- a/examples/data-objects/webflow/src/host/index.ts +++ b/examples/data-objects/webflow/src/host/index.ts @@ -3,5 +3,5 @@ * Licensed under the MIT License. */ -export { WebFlow } from "./webFlow"; -export { WebflowView } from "./webflowView"; +export { WebFlow } from "./webFlow.js"; +export { WebflowView } from "./webflowView.js"; diff --git a/examples/data-objects/webflow/src/host/searchmenu/index.ts b/examples/data-objects/webflow/src/host/searchmenu/index.ts index aced41ddd702..a4d0397dc466 100644 --- a/examples/data-objects/webflow/src/host/searchmenu/index.ts +++ b/examples/data-objects/webflow/src/host/searchmenu/index.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. */ -import { Dom, ICommand, KeyCode, randomId } from "../../util"; -import { debug } from "../debug"; -import { View } from "./view"; +import { Dom, ICommand, KeyCode, randomId } from "../../util/index.js"; +import { debug } from "../debug.js"; +import { View } from "./view.js"; // eslint-disable-next-line import/no-unassigned-import import "./index.css"; diff --git a/examples/data-objects/webflow/src/host/webFlow.ts b/examples/data-objects/webflow/src/host/webFlow.ts index df9272a2708d..d9de802385a0 100644 --- a/examples/data-objects/webflow/src/host/webFlow.ts +++ b/examples/data-objects/webflow/src/host/webFlow.ts @@ -13,8 +13,8 @@ import { LazyLoadedDataObject, } from "@fluidframework/data-object-base"; import { ISharedDirectory, SharedDirectory } from "@fluidframework/map"; -import { FlowDocument } from "../document"; -import { hostType } from "../package"; +import { FlowDocument } from "../document/index.js"; +import { hostType } from "../package.js"; export class WebFlow extends LazyLoadedDataObject { private static readonly factory = new LazyLoadedDataObjectFactory( diff --git a/examples/data-objects/webflow/src/host/webflowView.tsx b/examples/data-objects/webflow/src/host/webflowView.tsx index cbfa1c20d4c4..e088c7bf735c 100644 --- a/examples/data-objects/webflow/src/host/webflowView.tsx +++ b/examples/data-objects/webflow/src/host/webflowView.tsx @@ -5,17 +5,17 @@ import React, { KeyboardEventHandler, useEffect, useRef, useState } from "react"; -import { FlowDocument } from "../document"; -import { Editor } from "../editor"; -import { htmlFormatter } from "../html/formatters"; -import { ICommand, TagName } from "../util"; -import { IFormatterState, RootFormatter } from "../view/formatter"; -import { debug } from "./debug"; +import { FlowDocument } from "../document/index.js"; +import { Editor } from "../editor/index.js"; +import { htmlFormatter } from "../html/formatters.js"; +import { ICommand, TagName } from "../util/index.js"; +import { IFormatterState, RootFormatter } from "../view/formatter.js"; +import { debug } from "./debug.js"; // eslint-disable-next-line import/no-unassigned-import import "./index.css"; // eslint-disable-next-line import/no-unassigned-import import "./debug.css"; -import { SearchMenuView } from "./searchmenu"; +import { SearchMenuView } from "./searchmenu/index.js"; const always = () => true; diff --git a/examples/data-objects/webflow/src/html/css.ts b/examples/data-objects/webflow/src/html/css.ts index 4da7da953170..93ee23f1f7b5 100644 --- a/examples/data-objects/webflow/src/html/css.ts +++ b/examples/data-objects/webflow/src/html/css.ts @@ -4,8 +4,8 @@ */ import { ISegment } from "@fluidframework/merge-tree"; -import { getCss } from "../document"; -import { areStringsEquivalent } from "../util"; +import { getCss } from "../document/index.js"; +import { areStringsEquivalent } from "../util/index.js"; // Note: Similar to TokenList.set(..), but elides the search for duplicate tokens. const concat = (leftTokens: string, rightTokens: string) => diff --git a/examples/data-objects/webflow/src/html/debug.ts b/examples/data-objects/webflow/src/html/debug.ts index ddbef5a4ab23..715435e80019 100644 --- a/examples/data-objects/webflow/src/html/debug.ts +++ b/examples/data-objects/webflow/src/html/debug.ts @@ -4,6 +4,6 @@ */ import { IDebugger } from "debug"; -import { debug as parent } from "../debug"; +import { debug as parent } from "../debug.js"; export const debug: IDebugger = parent.extend("html"); diff --git a/examples/data-objects/webflow/src/html/formatters.ts b/examples/data-objects/webflow/src/html/formatters.ts index 693c5c8d4930..0ea43341c2a1 100644 --- a/examples/data-objects/webflow/src/html/formatters.ts +++ b/examples/data-objects/webflow/src/html/formatters.ts @@ -5,14 +5,14 @@ import { assert } from "@fluidframework/common-utils"; import { Marker, TextSegment } from "@fluidframework/merge-tree"; -import { DocSegmentKind, getCss, getDocSegmentKind } from "../document"; -import { emptyObject, TagName } from "../util"; -import { getAttrs, syncAttrs } from "../util/attr"; - -import { Formatter, IFormatterState, RootFormatter } from "../view/formatter"; -import { Layout } from "../view/layout"; -import { ICssProps, sameCss, syncCss } from "./css"; -import { debug } from "./debug"; +import { DocSegmentKind, getCss, getDocSegmentKind } from "../document/index.js"; +import { emptyObject, TagName } from "../util/index.js"; +import { getAttrs, syncAttrs } from "../util/attr.js"; + +import { Formatter, IFormatterState, RootFormatter } from "../view/formatter.js"; +import { Layout } from "../view/layout.js"; +import { ICssProps, sameCss, syncCss } from "./css.js"; +import { debug } from "./debug.js"; class HtmlFormatter extends RootFormatter { public begin(layout: Layout) { diff --git a/examples/data-objects/webflow/src/index.ts b/examples/data-objects/webflow/src/index.ts index a86d365d0631..89e0d345da72 100644 --- a/examples/data-objects/webflow/src/index.ts +++ b/examples/data-objects/webflow/src/index.ts @@ -6,10 +6,10 @@ import { ContainerViewRuntimeFactory } from "@fluid-example/example-utils"; import React from "react"; -export { FlowDocument } from "./document"; -export { Editor } from "./editor"; -import { WebFlow, WebflowView } from "./host"; -export { htmlFormatter } from "./html/formatters"; +export { FlowDocument } from "./document/index.js"; +export { Editor } from "./editor/index.js"; +import { WebFlow, WebflowView } from "./host/index.js"; +export { htmlFormatter } from "./html/formatters.js"; const webFlowViewCallback = (webFlow: WebFlow) => React.createElement(WebflowView, { docP: webFlow.getFlowDocument() }); diff --git a/examples/data-objects/webflow/src/package.ts b/examples/data-objects/webflow/src/package.ts index 1aacba61194b..88d07d81fa1a 100644 --- a/examples/data-objects/webflow/src/package.ts +++ b/examples/data-objects/webflow/src/package.ts @@ -3,12 +3,9 @@ * Licensed under the MIT License. */ -/* eslint-disable @typescript-eslint/no-var-requires */ -// eslint-disable-next-line @typescript-eslint/no-require-imports -const { name, version } = require("../package.json"); -/* eslint-enable @typescript-eslint/no-var-requires */ +import { pkgName, pkgVersion } from "./packageVersion.js"; -const makeTypeName = (type: string) => `${name}/${type}@${version}`; +const makeTypeName = (type: string) => `${pkgName}/${type}@${pkgVersion}`; export const hostType = makeTypeName("host"); export const documentType = makeTypeName("flow-document"); diff --git a/examples/data-objects/webflow/src/packageVersion.ts b/examples/data-objects/webflow/src/packageVersion.ts new file mode 100644 index 000000000000..020852ec61bb --- /dev/null +++ b/examples/data-objects/webflow/src/packageVersion.ts @@ -0,0 +1,9 @@ +/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + * + * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY + */ + +export const pkgName = "@fluid-example/webflow"; +export const pkgVersion = "2.0.0-internal.5.1.0"; diff --git a/examples/data-objects/webflow/src/test/.mocharc.js b/examples/data-objects/webflow/src/test/.mocharc.cjs similarity index 80% rename from examples/data-objects/webflow/src/test/.mocharc.js rename to examples/data-objects/webflow/src/test/.mocharc.cjs index 24d877223ed9..7bb530ceecdb 100644 --- a/examples/data-objects/webflow/src/test/.mocharc.js +++ b/examples/data-objects/webflow/src/test/.mocharc.cjs @@ -8,5 +8,5 @@ const packageDir = `${__dirname}/../..`; const getFluidTestMochaConfig = require("@fluid-internal/test-version-utils/mocharc-common.js"); -const config = getFluidTestMochaConfig(packageDir, ["ignore-styles"]); +const config = getFluidTestMochaConfig(packageDir); module.exports = config; diff --git a/examples/data-objects/webflow/src/test/direction.spec.ts b/examples/data-objects/webflow/src/test/direction.spec.ts index a9677c81d790..6ce7cea2fc15 100644 --- a/examples/data-objects/webflow/src/test/direction.spec.ts +++ b/examples/data-objects/webflow/src/test/direction.spec.ts @@ -4,7 +4,7 @@ */ import { strict as assert } from "assert"; -import { Direction, getDeltaX, getDeltaY } from "../util"; +import { Direction, getDeltaX, getDeltaY } from "../util/index.js"; const cases = [ { name: "none", direction: Direction.none, expectedX: 0, expectedY: 0 }, diff --git a/examples/data-objects/webflow/src/test/flowdocument.spec.ts b/examples/data-objects/webflow/src/test/flowdocument.spec.ts index 29901fe0fd07..34265b0aede3 100644 --- a/examples/data-objects/webflow/src/test/flowdocument.spec.ts +++ b/examples/data-objects/webflow/src/test/flowdocument.spec.ts @@ -8,8 +8,8 @@ import { Marker, ReferenceType } from "@fluidframework/merge-tree"; import { requestFluidObject } from "@fluidframework/runtime-utils"; import { ITestObjectProvider } from "@fluidframework/test-utils"; import { describeLoaderCompat } from "@fluid-internal/test-version-utils"; -import { FlowDocument } from "../document"; -import { TagName } from "../util"; +import { FlowDocument } from "../document/index.js"; +import { TagName } from "../util/index.js"; describeLoaderCompat("FlowDocument", (getTestObjectProvider) => { let doc: FlowDocument; diff --git a/examples/data-objects/webflow/src/test/layout.spec.ts b/examples/data-objects/webflow/src/test/layout.spec.ts index d4653289a418..68770722d334 100644 --- a/examples/data-objects/webflow/src/test/layout.spec.ts +++ b/examples/data-objects/webflow/src/test/layout.spec.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. */ -// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports -require("jsdom-global")("", { url: "http://localhost" }); +// eslint-disable-next-line import/no-unassigned-import +import "jsdom-global/register.js"; window.performance.mark ??= () => undefined as PerformanceMark; window.performance.measure ??= () => undefined as PerformanceMeasure; @@ -12,9 +12,9 @@ import { strict as assert } from "assert"; import { requestFluidObject } from "@fluidframework/runtime-utils"; import { ITestObjectProvider } from "@fluidframework/test-utils"; import { describeLoaderCompat } from "@fluid-internal/test-version-utils"; -import { htmlFormatter } from ".."; -import { FlowDocument } from "../document"; -import { Layout } from "../view/layout"; +import { htmlFormatter } from "../index.js"; +import { FlowDocument } from "../document/index.js"; +import { Layout } from "../view/layout.js"; interface ISnapshotNode { node: Node; diff --git a/examples/data-objects/webflow/src/test/segmentspan.spec.ts b/examples/data-objects/webflow/src/test/segmentspan.spec.ts index ae2a49e70c12..6a67995a03c5 100644 --- a/examples/data-objects/webflow/src/test/segmentspan.spec.ts +++ b/examples/data-objects/webflow/src/test/segmentspan.spec.ts @@ -8,8 +8,8 @@ import { TextSegment } from "@fluidframework/merge-tree"; import { requestFluidObject } from "@fluidframework/runtime-utils"; import { ITestObjectProvider } from "@fluidframework/test-utils"; import { describeLoaderCompat } from "@fluid-internal/test-version-utils"; -import { FlowDocument } from "../document"; -import { SegmentSpan } from "../document/segmentspan"; +import { FlowDocument } from "../document/index.js"; +import { SegmentSpan } from "../document/segmentspan.js"; describeLoaderCompat("SegmentSpan", (getTestObjectProvider) => { let doc: FlowDocument; diff --git a/examples/data-objects/webflow/src/test/strings.spec.ts b/examples/data-objects/webflow/src/test/strings.spec.ts index 46a359da9e95..78585470173d 100644 --- a/examples/data-objects/webflow/src/test/strings.spec.ts +++ b/examples/data-objects/webflow/src/test/strings.spec.ts @@ -4,7 +4,7 @@ */ import { strict as assert } from "assert"; -import { areStringsEquivalent } from "../util"; +import { areStringsEquivalent } from "../util/index.js"; function test(left: string | undefined | null, right: string | undefined | null) { const isLeftEmpty = left === "" || left === null || left === undefined; diff --git a/examples/data-objects/webflow/src/test/tokenlist.spec.ts b/examples/data-objects/webflow/src/test/tokenlist.spec.ts index 7c0cef773d1c..cc79e62fb08e 100644 --- a/examples/data-objects/webflow/src/test/tokenlist.spec.ts +++ b/examples/data-objects/webflow/src/test/tokenlist.spec.ts @@ -4,7 +4,7 @@ */ import { strict as assert } from "assert"; -import { findToken, TokenList } from "../util"; +import { findToken, TokenList } from "../util/index.js"; describe("TokenList", () => { describe("findToken", () => { diff --git a/examples/data-objects/webflow/src/test/tsconfig.json b/examples/data-objects/webflow/src/test/tsconfig.json index 25d764cecba1..c2515626f396 100644 --- a/examples/data-objects/webflow/src/test/tsconfig.json +++ b/examples/data-objects/webflow/src/test/tsconfig.json @@ -8,6 +8,7 @@ "declaration": false, "declarationMap": false, "strictNullChecks": false, + "module": "esnext", }, "include": ["./**/*"], "references": [ diff --git a/examples/data-objects/webflow/src/util/attr.ts b/examples/data-objects/webflow/src/util/attr.ts index 45f58da016bd..f769a1c697f3 100644 --- a/examples/data-objects/webflow/src/util/attr.ts +++ b/examples/data-objects/webflow/src/util/attr.ts @@ -4,8 +4,8 @@ */ import { ISegment } from "@fluidframework/merge-tree"; -import { areStringsEquivalent } from "./string"; -import { emptyObject } from "./"; +import { areStringsEquivalent } from "./string.js"; +import { emptyObject } from "./index.js"; export interface IHTMLAttributes { src?: string; diff --git a/examples/data-objects/webflow/src/util/caret.ts b/examples/data-objects/webflow/src/util/caret.ts index b4f0f32f3eef..d6c3ace39a04 100644 --- a/examples/data-objects/webflow/src/util/caret.ts +++ b/examples/data-objects/webflow/src/util/caret.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. */ -import { Direction, getTabDirection } from "./direction"; -import { IRect } from "./rect"; +import { Direction, getTabDirection } from "./direction.js"; +import { IRect } from "./rect.js"; export type ICaretBounds = Pick; diff --git a/examples/data-objects/webflow/src/util/index.ts b/examples/data-objects/webflow/src/util/index.ts index 0f4e01376417..8f5943d93cb5 100644 --- a/examples/data-objects/webflow/src/util/index.ts +++ b/examples/data-objects/webflow/src/util/index.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. */ -export { ICaretBounds, ICaretEvent, CaretEventType, caretEnter, caretLeave } from "./caret"; -export { ICommand } from "./command"; -export { Direction, getDeltaX, getDeltaY, getTabDirection, TabDirection } from "./direction"; -export { Dom } from "./dom"; -export { KeyCode } from "./keycode"; -export { randomId } from "./random"; -export { IRect, Rect } from "./rect"; -export { getSegmentRange } from "./segment"; -export { areStringsEquivalent } from "./string"; -export { hasTagName, isElementNode, isTextNode, TagName } from "./tagName"; -export { findToken, TokenList } from "./tokenlist"; +export { ICaretBounds, ICaretEvent, CaretEventType, caretEnter, caretLeave } from "./caret.js"; +export { ICommand } from "./command.js"; +export { Direction, getDeltaX, getDeltaY, getTabDirection, TabDirection } from "./direction.js"; +export { Dom } from "./dom.js"; +export { KeyCode } from "./keycode.js"; +export { randomId } from "./random.js"; +export { IRect, Rect } from "./rect.js"; +export { getSegmentRange } from "./segment.js"; +export { areStringsEquivalent } from "./string.js"; +export { hasTagName, isElementNode, isTextNode, TagName } from "./tagName.js"; +export { findToken, TokenList } from "./tokenlist.js"; export const done = Promise.resolve(); export const emptyObject = Object.freeze({}); diff --git a/examples/data-objects/webflow/src/util/localref.ts b/examples/data-objects/webflow/src/util/localref.ts index be883d5f4063..747619a68550 100644 --- a/examples/data-objects/webflow/src/util/localref.ts +++ b/examples/data-objects/webflow/src/util/localref.ts @@ -4,8 +4,8 @@ */ import { LocalReferencePosition } from "@fluidframework/merge-tree"; -import { debug } from "../document/debug"; -import { FlowDocument } from "../document/index"; +import { debug } from "../document/debug.js"; +import { FlowDocument } from "../document/index.js"; export function updateRef(doc: FlowDocument, ref: LocalReferencePosition, position: number) { if (isNaN(position)) { diff --git a/examples/data-objects/webflow/src/util/tag.ts b/examples/data-objects/webflow/src/util/tag.ts index 1da18e17336b..93a5f8ebb84f 100644 --- a/examples/data-objects/webflow/src/util/tag.ts +++ b/examples/data-objects/webflow/src/util/tag.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. */ -import { DocSegmentKind } from "../document"; -import { TagName } from "./tagName"; +import { DocSegmentKind } from "../document/index.js"; +import { TagName } from "./tagName.js"; const segmentKindToIdPrefix = { [DocSegmentKind.beginTags]: "b:", diff --git a/examples/data-objects/webflow/src/util/tokenlist.ts b/examples/data-objects/webflow/src/util/tokenlist.ts index dae53f5e2267..8d8bd610a169 100644 --- a/examples/data-objects/webflow/src/util/tokenlist.ts +++ b/examples/data-objects/webflow/src/util/tokenlist.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -import { CharCode } from "./charcode"; +import { CharCode } from "./charcode.js"; export function findToken(tokenList: string, token: string) { if (tokenList) { diff --git a/examples/data-objects/webflow/src/view/debug.ts b/examples/data-objects/webflow/src/view/debug.ts index cab7c5be3ad5..b4b5e516cc1c 100644 --- a/examples/data-objects/webflow/src/view/debug.ts +++ b/examples/data-objects/webflow/src/view/debug.ts @@ -4,6 +4,6 @@ */ import { IDebugger } from "debug"; -import { debug as parent } from "../debug"; +import { debug as parent } from "../debug.js"; export const debug: IDebugger = parent.extend("view"); diff --git a/examples/data-objects/webflow/src/view/formatter.ts b/examples/data-objects/webflow/src/view/formatter.ts index 77f3ed7cc1af..3d879d08b31f 100644 --- a/examples/data-objects/webflow/src/view/formatter.ts +++ b/examples/data-objects/webflow/src/view/formatter.ts @@ -4,8 +4,8 @@ */ import { SequenceEvent } from "@fluidframework/sequence"; -import { emptyObject } from "../util"; -import { Layout } from "./layout"; +import { emptyObject } from "../util/index.js"; +import { Layout } from "./layout.js"; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface IFormatterState {} diff --git a/examples/data-objects/webflow/src/view/layout.ts b/examples/data-objects/webflow/src/view/layout.ts index 491ef4e218c2..d25c88bdf90f 100644 --- a/examples/data-objects/webflow/src/view/layout.ts +++ b/examples/data-objects/webflow/src/view/layout.ts @@ -13,7 +13,7 @@ import { LocalReferencePosition, } from "@fluidframework/merge-tree"; import { SequenceEvent } from "@fluidframework/sequence"; -import { FlowDocument } from "../document"; +import { FlowDocument } from "../document/index.js"; import { clamp, Dom, @@ -23,10 +23,10 @@ import { hasTagName, isTextNode, TagName, -} from "../util"; -import { extractRef, updateRef } from "../util/localref"; -import { debug } from "./debug"; -import { BootstrapFormatter, Formatter, IFormatterState, RootFormatter } from "./formatter"; +} from "../util/index.js"; +import { extractRef, updateRef } from "../util/localref.js"; +import { debug } from "./debug.js"; +import { BootstrapFormatter, Formatter, IFormatterState, RootFormatter } from "./formatter.js"; interface ILayoutCursor { parent: Node; diff --git a/examples/data-objects/webflow/tsconfig.esnext.json b/examples/data-objects/webflow/tsconfig.esnext.json deleted file mode 100644 index 1ea62d46b20b..000000000000 --- a/examples/data-objects/webflow/tsconfig.esnext.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "outDir": "./lib", - "module": "esnext", - }, -} diff --git a/examples/data-objects/webflow/tsconfig.json b/examples/data-objects/webflow/tsconfig.json index 10f80581ad10..633e32ea8b9a 100644 --- a/examples/data-objects/webflow/tsconfig.json +++ b/examples/data-objects/webflow/tsconfig.json @@ -6,8 +6,14 @@ "jsx": "react", "strictNullChecks": false, "rootDir": "./src", + // Note: despite being esnext, this publishes to "dist" over "lib" to keep root project settings' debug launch targets + // reasonable (source map config takes a dependency on executed JS being in "dist" for mocha tests). + // If we convert the remaining group of client packages to use esnext modules only, + // we likely want to standardize the esnext build directory. "outDir": "./dist", "types": ["node", "react", "react-dom"], + "module": "esnext", + "resolveJsonModule": true, }, "include": ["src/**/*"], } diff --git a/examples/data-objects/webflow/webpack.config.js b/examples/data-objects/webflow/webpack.config.cjs similarity index 84% rename from examples/data-objects/webflow/webpack.config.js rename to examples/data-objects/webflow/webpack.config.cjs index 2c51e33db7af..4eca9a9f2943 100644 --- a/examples/data-objects/webflow/webpack.config.js +++ b/examples/data-objects/webflow/webpack.config.cjs @@ -17,6 +17,12 @@ module.exports = (env) => { entry: "./src/index.ts", resolve: { extensions: [".mjs", ".ts", ".tsx", ".js"], + // This ensures that webpack understands fully-specified relative module imports. + // See https://github.com/webpack/webpack/issues/13252 for more discussion. + extensionAlias: { + ".js": [".ts", ".tsx", ".js"], + ".mjs": [".mts", ".mtsx", ".mjs"], + }, fallback: { dgram: false, fs: false, @@ -64,7 +70,7 @@ module.exports = (env) => { ignored: "**/node_modules/**", }, }, - isProduction ? require("./webpack.prod") : require("./webpack.dev"), + isProduction ? require("./webpack.prod.cjs") : require("./webpack.dev.cjs"), fluidRoute.devServerConfig(__dirname, env), ); }; diff --git a/examples/data-objects/webflow/webpack.dev.js b/examples/data-objects/webflow/webpack.dev.cjs similarity index 100% rename from examples/data-objects/webflow/webpack.dev.js rename to examples/data-objects/webflow/webpack.dev.cjs diff --git a/examples/data-objects/webflow/webpack.prod.js b/examples/data-objects/webflow/webpack.prod.cjs similarity index 100% rename from examples/data-objects/webflow/webpack.prod.js rename to examples/data-objects/webflow/webpack.prod.cjs diff --git a/packages/test/test-version-utils/src/versionUtils.ts b/packages/test/test-version-utils/src/versionUtils.ts index 78cd2b5d8d73..f20d3badd757 100644 --- a/packages/test/test-version-utils/src/versionUtils.ts +++ b/packages/test/test-version-utils/src/versionUtils.ts @@ -5,7 +5,7 @@ /* Utilities to manage finding, installing and loading legacy versions */ -import { exec, execSync } from "child_process"; +import { ExecOptions, exec, execSync } from "child_process"; import * as path from "path"; import { existsSync, mkdirSync, rmdirSync, readdirSync, readFileSync, writeFileSync } from "fs"; @@ -200,9 +200,18 @@ export async function ensureInstalled( // Check installed status again under lock the modulePath lock if (force || !(await isInstalled(version))) { + const options: ExecOptions = { + cwd: modulePath, + env: { + ...process.env, + // Reset any parent process node options: path-specific options (ex: --require, --experimental-loader) + // will otherwise propagate to these commands but fail to resolve. + NODE_OPTIONS: "", + }, + }; // Install the packages await new Promise((resolve, reject) => - exec(`npm init --yes`, { cwd: modulePath }, (error, stdout, stderr) => { + exec(`npm init --yes`, options, (error, stdout, stderr) => { if (error) { reject(new Error(`Failed to initialize install directory ${modulePath}`)); } @@ -214,7 +223,7 @@ export async function ensureInstalled( `npm i --no-package-lock ${adjustedPackageList .map((pkg) => `${pkg}@${version}`) .join(" ")}`, - { cwd: modulePath }, + options, (error, stdout, stderr) => { if (error) { reject(new Error(`Failed to install in ${modulePath}\n${stderr}`)); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8f4c7f268554..12c600351c45 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4588,15 +4588,15 @@ importers: eslint: specifier: ~8.6.0 version: 8.6.0 + esm-loader-css: + specifier: ^1.0.4 + version: 1.0.4 file-loader: specifier: ^3.0.1 version: 3.0.1(webpack@5.83.0) html-loader: specifier: ^3.1.0 version: 3.1.2(webpack@5.83.0) - ignore-styles: - specifier: ^5.0.1 - version: 5.0.1 jsdom: specifier: ^16.7.0 version: 16.7.0 @@ -31387,6 +31387,13 @@ packages: capture-stack-trace: 1.0.2 dev: true + /create-esm-loader@0.2.3: + resolution: {integrity: sha512-cllzD6IU/mzXBs5OdQVWL3+ne5Elpu3Wdm7h5OldMbGXk76yr9XzHlQXWJ4zfs0ZAibe26rkbs4KvMAJm7fIZA==} + engines: {node: '>=14.x'} + dependencies: + semver: 7.5.1 + dev: true + /create-hash@1.2.0: resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} dependencies: @@ -33540,6 +33547,14 @@ packages: - supports-color dev: true + /esm-loader-css@1.0.4: + resolution: {integrity: sha512-4tzPASNlPSd+m+LmDMxGm/2vvKKooTS2tWY+0pUTvnacN05eQE8afJZujrobSmMRUtvdys2/BjHz9mq/D2i+Ow==} + dependencies: + create-esm-loader: 0.2.3 + npm-run-all: 4.1.5 + semver: 7.5.1 + dev: true + /espree@9.5.2: resolution: {integrity: sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -36118,10 +36133,6 @@ packages: resolution: {integrity: sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==} dev: true - /ignore-styles@5.0.1: - resolution: {integrity: sha512-gQQmIznCETPLEzfg1UH4Cs2oRq+HBPl8quroEUNXT8oybEG7/0lqI3dGgDSRry6B9HcCXw3PVkFFS0FF3CMddg==} - dev: true - /ignore-walk@4.0.1: resolution: {integrity: sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==} engines: {node: '>=10'}