Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[UIE-94] Multi-package MVP #3992

Merged
merged 20 commits into from
Aug 3, 2023
Merged

[UIE-94] Multi-package MVP #3992

merged 20 commits into from
Aug 3, 2023

Conversation

nawatts
Copy link
Contributor

@nawatts nawatts commented Jul 6, 2023

https://broadworkbench.atlassian.net/browse/UIE-94

This sets up the infrastructure for and begins splitting up Terra UI into multiple packages. As a proof of concept / MVP, this extracts the Interactive component into @terra-ui-packages/components and the contents of src/libs/type-utils into @terra-ui-packages/core-utils.


There are several pieces involved in letting us import { Interactive } from '@terra-ui-packages/components' in Terra UI application code...

Yarn workspaces are used to have imports from @terra-ui-packages/components in Terra UI resolve to that package's directory within the packages directory (./packages/components).
https://yarnpkg.com/features/workspaces

exports, module, and main configuration in each package's package.json file control how imports of the package are resolved to a file. Each package is configured to have two versions: an ES Module version in lib/es/index.js and a CommonJS version in lib/cjs/index.js. We want the ES Module version to use with a bundler (Vite) and the CommonJS version to use with Jest (since Jest doesn't yet support modules). So with the package.json configuration, import { ... } from '@terra-ui-packages/components' resolves to ./packages/components/lib/es/index.js and require('@terra-ui-packages/components') resolves to ./packages/components/lib/cjs/index.js.
https://www.typescriptlang.org/docs/handbook/esm-node.html#packagejson-exports-imports-and-self-referencing

The files in the lib directory are outputs from a build. Each package has a build script configured in package.json. That build script runs tsc twice: once to produce the ES Module output and once to produce the CommonJS output. Those outputs are configured in two TS project files: tsconfig.json has configuration for ES Modules and contains any package specific TS settings that are needed. tsconfig.cjs.json extends tsconfig.json and overrides the module and outDir settings. Each package's tsconfig.json extends from tsconfig.base.json at the workspace root so that all packages can share common base compiler settings.

There's also a tsconfig.json at the workspace root. Currently, it has two functions... It serves as the TS project for the Terra UI application itself (ideally, the application would be a separate package instead of mingled with the workspace root). It also contains references to the TS projects for each package. This allows building all packages from the workspace root with tsc --build.
https://www.typescriptlang.org/docs/handbook/project-references.html

To have changes to packages code automatically built and reloaded in the browser, as we're used to with application code, the start script now runs two things: the Vite dev server and tsc --build --watch.


Changes to development workflow:

  • yarn run build does not change
  • yarn start is used as before, though now it runs an additional process (tsc --build --watch)
  • yarn test ...
    • No longer runs all tests in the repo. It now runs only tests for the Terra UI application.
    • Terra UI relies on the build output of packages, so some care must be taken to ensure that package builds are up to date before running Terra UI tests to ensure that the current packages code is being tested. Also, a build has to be run (yarn run build or yarn start) before running tests for the first time.

To run a script for a specific package, use yarn workspace. For example, to run tests for core-utils, use yarn workspace '@terra-ui-packages/core-utils' test.
https://yarnpkg.com/cli/workspace

To run a script for all packages, use yarn workspaces foreach. For example, to run unit tests for all packages and Terra UI, use yarn workspaces foreach --exclude terra-integration-tests run test.
https://yarnpkg.com/cli/workspaces/foreach

@broadbot broadbot temporarily deployed to pr-10 July 6, 2023 17:13 Inactive
@broadbot broadbot temporarily deployed to pr-10 July 6, 2023 17:28 Inactive
@broadbot broadbot temporarily deployed to pr-10 July 6, 2023 17:59 Inactive
@broadbot broadbot temporarily deployed to pr-10 July 6, 2023 18:08 Inactive
@broadbot broadbot temporarily deployed to pr-10 July 10, 2023 19:32 Inactive
@broadbot broadbot temporarily deployed to pr-10 July 10, 2023 20:28 Inactive
@broadbot broadbot temporarily deployed to pr-10 July 10, 2023 20:46 Inactive
Comment on lines +230 to -233
- run: yarn run build
- run: yarn eslint --max-warnings=0 .
- run: yarn test --coverage --maxWorkers=2
- run: yarn workspaces foreach --exclude terra-integration-tests run test --coverage --maxWorkers=2 --passWithNoTests
- run: yarn check-types
- run: DISABLE_ESLINT_PLUGIN=true yarn build # already linted above
Copy link
Contributor Author

Choose a reason for hiding this comment

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

ESLint and tests require the packages to be built first so that imports from the packages can be resolved.

@@ -13,4 +13,4 @@ node_modules
.vscode
coverage
vite.config.js
jest/*
packages/*/lib
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ignore build outputs.

@@ -5,9 +5,12 @@ node_modules

# testing
/coverage
packages/*/coverage
Copy link
Contributor Author

Choose a reason for hiding this comment

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

A coverage report is generated for each package.


# production
/build
packages/*/lib
tsconfig.tsbuildinfo
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ignore build outputs.

Comment on lines +47 to +22
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This plugin provides yarn workspaces foreach ....
https://yarnpkg.com/cli/workspaces/foreach

@@ -1,7 +1,9 @@
FROM node:18.10

COPY . /terra-ui/
RUN cd /terra-ui && yarn build
RUN cd terra-ui \
&& PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true yarn install --immutable \
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Run yarn install to build "unplugged" packages (such as ESBuild).

Without PUPPETEER_SKIP_CHROMIUM_DOWNLOAD =true, Puppeteer's build failed because "The chromium binary is not available for arm64.".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yarn install is currently being run from the yarn build script, but this PR removes it from that step.

Comment on lines +11 to +16
"@terra-ui-packages/components": "*",
"@terra-ui-packages/core-utils": "*",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The Terra UI app depends on the latest code in each package. Yarn resolves imports from these packages to the files in the packages directory.

@@ -64,20 +66,22 @@
},
"scripts": {
"analyze": "yarn build && source-map-explorer 'build/static/js/*.js' --gzip",
"build": "yarn install && env $(yarn setenv) vite build && rm build/config.json",
"build": "tsc --build && env $(yarn setenv) vite build && rm build/config.json",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

tsc --build builds each package and checks types. The packages must be built so that Vite can resolve imports from them.

"serve": "vite preview",
"start": "env $(yarn setenv) vite",
"start": "concurrently \"tsc --build --watch\" \"env $(yarn setenv) vite\"",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

tsc --build --watch watches for changes to packages code and rebuilds them. vite watches for changes to Terra UI code and packages build output and rebuilds Terra UI.

"check-types": "check-dts types/**/*.ts",
"test": "jest",
"check-dts": "check-dts types/**/*.ts",
"check-types": "yarn workspaces foreach run check-dts",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@terra-ui-packages/core-utils includes type helpers and also has a check-dts script to check those.

@broadbot broadbot temporarily deployed to pr-10 July 10, 2023 20:57 Inactive
"version": "0.0.1",
"private": true,
"scripts": {
"build": "tsc && tsc -p tsconfig.cjs.json",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We build two different versions of each package (ES Modules and CommonJS).

Copy link
Contributor

Choose a reason for hiding this comment

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

Should this have a comment inline describing why we need two versions?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Off the top of my head, I'm not sure if NPM / Yarn can tolerate comments in package.json files. This could probably be clarified some by having the first tsc explicitly state the config file (tsconfig.json) vs relying on the default. The tsconfig files have comments about what they are for.

Comment on lines +9 to +17
"type": "module",
"module": "./lib/es/index.js",
"main": "./lib/cjs/index.js",
"exports": {
".": {
"import": "./lib/es/index.js",
"require": "./lib/cjs/index.js"
}
},
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Configure exports for using this package via import (ES Modules) or require (CommonJS). The package API is the same either way, but they resolve to different files.

Comment on lines +18 to +21
"files": [
"lib/cjs/**",
"lib/es/**"
],
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Only include build outputs in the published package.

@@ -0,0 +1,3 @@
export const delay = (ms: number): Promise<void> => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

delay was pulled into this PR because a type-utils test depends on it.

type LoadedState<S, E = Error> = NoneState | LoadingState<S> | ReadyState<S> | ErrorState<S, E>;

export default LoadedState;
export type LoadedState<S, E = Error> = NoneState | LoadingState<S> | ReadyState<S> | ErrorState<S, E>;
Copy link
Contributor Author

@nawatts nawatts Jul 10, 2023

Choose a reason for hiding this comment

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

Since all imports will be from the package (import { LoadedState } from '@terra-ui-packages/core-utils';), all exports must be named.

"rootDir": "src"
},
"include": ["src"],
"exclude": ["src/**/*.types.ts", "src/**/*.errors.ts"]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Exclude the files used by check-dts. lodash-fp-types.errors.ts intentionally includes invalid code and if it were included, the project would not build.

sonar.javascript.lcov.reportPaths=coverage/lcov.info
sonar.javascript.lcov.reportPaths=coverage/lcov.info,packages/*/coverage/lcov.info
Copy link
Contributor Author

@nawatts nawatts Jul 10, 2023

Choose a reason for hiding this comment

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

Each package now has its own coverage report. Upload them all to SonarCloud.


# Only scan code under src.
sonar.sources=src
sonar.sources=packages,src
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The packages directory now also contains source code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Base TypeScript configuration for all packages and Terra UI itself.

@nawatts
Copy link
Contributor Author

nawatts commented Aug 2, 2023

Rebased to resolve merge conflict.

@nawatts nawatts requested a review from cahrens August 2, 2023 21:43
@broadbot broadbot temporarily deployed to pr-10 August 2, 2023 21:50 Inactive
@@ -16,7 +16,9 @@ module.exports = {
}
},
wrapScriptExecution(executor, project, locator, scriptName) {
Copy link
Contributor Author

@nawatts nawatts Aug 2, 2023

Choose a reason for hiding this comment

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

This is what shows the

╔══════════════════════════════════════════════════════════════════════════════╗
║ Be sure to copy a config/xxx.json to build/config.json if you're planning to ║
║ deploy this build.                                                           ║
╚══════════════════════════════════════════════════════════════════════════════╝

notice after running a build. We only want to show that on app builds, not package builds.

There's definitely easier ways to show this notice than a Yarn hook, but I'll leave it be here.

Copy link
Contributor

Choose a reason for hiding this comment

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

The copying over happens as part of our build process? I guess I'm wondering why we have this warning at all.

Copy link
Contributor Author

@nawatts nawatts Aug 3, 2023

Choose a reason for hiding this comment

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

Yes, it's done in CI.

- run: cp config/<< parameters.env >>.json build/config.json

https://github.com/DataBiosphere/saturn-ui-prod-deploy/blob/44b4565a4dc118482be63e12d6497ab62271fcb0/.circleci/config.yml#L76

Yeah, we could probably get rid of this altogether. Though it might be worth keeping just for the rare case of manual deploys.

Copy link
Contributor

Choose a reason for hiding this comment

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

That is exactly why it is there. If I had the time, I'd instead move this warning into an error message that shows up when the UI attempts to pull that file and finds it absent. This warning was added specifically because the mechanism for resolving that error isn't particularly obvious.

@broadbot broadbot temporarily deployed to pr-10 August 2, 2023 22:15 Inactive
@broadbot broadbot temporarily deployed to pr-10 August 2, 2023 23:00 Inactive
],
"dependencies": {
"lodash": "^4.17.21",
"react-hyperscript-helpers": "^2.0.0"
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see where it uses react-hyperscirpt-helpers.

Copy link
Contributor Author

@nawatts nawatts Aug 3, 2023

Choose a reason for hiding this comment

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

You're right, it doesn't. I think I intended to use it for Interactive, but then ran into a stumbling block: #3992 (comment).

I can remove it, but any future components added to this package will likely use it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would tend to not including it until it's actually used, but given that I was OK with leaving the test-utils dependency in even though it isn't used yet I guess it's not a big deal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed in 83b8c59.

@cahrens cahrens removed the request for review from marctalbott August 3, 2023 13:22
@sonarcloud
Copy link

sonarcloud bot commented Aug 3, 2023

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 4 Code Smells

26.7% 26.7% Coverage
4.8% 4.8% Duplication

warning The version of Java (11.0.11) you have used to run this analysis is deprecated and we will stop accepting it soon. Please update to at least Java 17.
Read more here

@broadbot broadbot temporarily deployed to pr-10 August 3, 2023 13:32 Inactive
@nawatts nawatts added this pull request to the merge queue Aug 3, 2023
Merged via the queue into dev with commit 14a0fa6 Aug 3, 2023
5 checks passed
@nawatts nawatts deleted the uie-94-multi-package branch August 3, 2023 17:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants