From fa3c4f36ca03a88cd16b10505fc82e5e7159d2ea Mon Sep 17 00:00:00 2001 From: Doug Parker Date: Fri, 17 Mar 2023 16:25:49 -0700 Subject: [PATCH] Migrates `//examples/external` to use Preact. Refs #71. The migration is relatively straightforward, with one major bug which needed to be addressed. The comment in `//:node_modules/@rules_prerender/preact` explains most of the context, but essentially we cannot allow `//packages/preact:pkg` to depend on `//:node_modules/rules_prerender` because this is an NPM peer dependency, not a normal dependency. External workspaces cannot `npm_link_package()` from `@rules_prerender//packages/preact:pkg` when there is a transitive dependency on `//:node_modules/rules_prerender` because it duplicates the `rules_prerender` package in addition to the `//:node_modules/rules_prerender` target in their own workspace. This introduces incompatibilities, since user code and `@rules_prerender/preact` would be importing different versions of the `rules_prerender` package. Instead, we depend on `//:node_modules/rules_prerender_types` which drops the JS implementation from the output to avoid duplicating the `rules_prerender` NPM package. However this alone means there is no dependency whatsoever between `@rules_prerender/preact` and `rules_prerender` and users have to manually depend on both, even when they only import `@rules_prerender/preact`. To address this, we use `deps` in `link_npm_package()` to add the dependency _at link time_. Doing so allows the same package to be linked in multiple places with different implementations of `rules_prerender`. In the `@rules_prerender` workspace, we link it to `@rules_prerender//:node_modules/rules_prerender` as built from HEAD. In other workspaces (such as `examples/external/`), users should link to the `//:node_modules/rules_prerender` of that workspace. This keeps a single definition of `rules_prerender` and always places it in the right location. --- BUILD.bazel | 18 ++++++++++++++++ examples/external/BUILD.bazel | 23 +++++++++++++++++++-- examples/external/component/BUILD.bazel | 7 +++++-- examples/external/component/component.mts | 18 ---------------- examples/external/component/component.tsx | 25 +++++++++++++++++++++++ examples/external/package.json | 3 ++- examples/external/site.mts | 25 ----------------------- examples/external/site.tsx | 18 ++++++++++++++++ examples/external/tsconfig.json | 4 +++- packages/preact/BUILD.bazel | 5 ++++- 10 files changed, 96 insertions(+), 50 deletions(-) delete mode 100644 examples/external/component/component.mts create mode 100644 examples/external/component/component.tsx delete mode 100644 examples/external/site.mts create mode 100644 examples/external/site.tsx diff --git a/BUILD.bazel b/BUILD.bazel index 449e4ee1..f9abab98 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -74,6 +74,24 @@ npm_link_package( name = "node_modules/@rules_prerender/preact", src = "//packages/preact:pkg", visibility = ["//:__subpackages__"], + + # `@rules_prerender/preact` has an NPM peer dep on `rules_prerender`. We + # need to supply this at link-time rather than when the `npm_package()` is + # built so different links can specify different `rules_prerender` packages. + # + # For example, `@rules_prerender//:node_modules/@rules_prerender/preact` + # needs a dependency on `@rules_prerender//:node_modules/rules_prerender`, + # but an external workspace linking the same `npm_package()` would need its + # `@external_wksp//:node_modules/@rules_prerender/preact` to depend on + # `@external_wksp//:node_modules/rules_prerender`. + # + # If we didn't do this and allowed `//packages/preact:pkg` to directly + # depend on `//:node_modules/rules_prerender`, it would duplicate the + # `rules_prerender` package at runtime. + # See: https://bazelbuild.slack.com/archives/CEZUUKQ6P/p1678741591216319 + deps = { + "//:.aspect_rules_js/node_modules/rules_prerender": "rules_prerender", + }, ) bzl_library( diff --git a/examples/external/BUILD.bazel b/examples/external/BUILD.bazel index fcf1a8ea..5ec2b625 100644 --- a/examples/external/BUILD.bazel +++ b/examples/external/BUILD.bazel @@ -1,3 +1,4 @@ +load("@aspect_bazel_lib//lib:copy_to_bin.bzl", "copy_to_bin") load("@aspect_rules_js//js:defs.bzl", "js_binary", "js_run_binary") load("@aspect_rules_js//npm:defs.bzl", "npm_link_package") load("@aspect_rules_ts//ts:defs.bzl", "ts_config", "ts_project") @@ -10,6 +11,11 @@ load( ) load("@rules_prerender_npm//:defs.bzl", "npm_link_all_packages") +copy_to_bin( + name = "package", + srcs = ["package.json"], +) + npm_link_all_packages(name = "node_modules") npm_link_package( @@ -27,6 +33,14 @@ link_prerender_component( package = ":node_modules/@rules_prerender/declarative_shadow_dom", visibility = ["//visibility:public"], ) +npm_link_package( + name = "node_modules/@rules_prerender/preact", + src = "@rules_prerender//packages/preact:pkg", + visibility = ["//visibility:public"], + deps = { + "//:.aspect_rules_js/node_modules/rules_prerender": "rules_prerender", + }, +) ts_config( name = "tsconfig", @@ -43,10 +57,15 @@ ts_config( prerender_pages( name = "site", - src = "site.mts", + src = "site.tsx", source_map = True, tsconfig = "//:tsconfig", - lib_deps = ["//:node_modules/rules_prerender"], + # Need `"type": "module"` to load `*.js` files output by `*.tsx` compilation. + data = [":package"], + lib_deps = [ + "//:node_modules/@rules_prerender/preact", + "//:node_modules/preact", + ], deps = ["//component"], ) diff --git a/examples/external/component/BUILD.bazel b/examples/external/component/BUILD.bazel index 707264ba..f5b70fdf 100644 --- a/examples/external/component/BUILD.bazel +++ b/examples/external/component/BUILD.bazel @@ -8,14 +8,17 @@ load( prerender_component( name = "component", - srcs = ["component.mts"], + srcs = ["component.tsx"], scripts = [":script"], styles = [":style"], resources = [":resources"], source_map = True, tsconfig = "//:tsconfig", visibility = ["//:__subpackages__"], - lib_deps = ["//:node_modules/rules_prerender"], + lib_deps = [ + "//:node_modules/@rules_prerender/preact", + "//:node_modules/preact", + ], deps = ["//:prerender_components/@rules_prerender/declarative_shadow_dom"], ) diff --git a/examples/external/component/component.mts b/examples/external/component/component.mts deleted file mode 100644 index 609f51c6..00000000 --- a/examples/external/component/component.mts +++ /dev/null @@ -1,18 +0,0 @@ -import { includeScript, inlineStyle } from 'rules_prerender'; -import { polyfillDeclarativeShadowDom } from '@rules_prerender/declarative_shadow_dom'; - -export function renderComponent(): string { - return ` - - - - `.trim(); -} diff --git a/examples/external/component/component.tsx b/examples/external/component/component.tsx new file mode 100644 index 00000000..c1ad52bc --- /dev/null +++ b/examples/external/component/component.tsx @@ -0,0 +1,25 @@ +import { VNode } from 'preact'; +import { Template, includeScript, inlineStyle } from '@rules_prerender/preact'; +import { polyfillDeclarativeShadowDom } from '@rules_prerender/declarative_shadow_dom/preact.mjs'; + +declare module 'preact' { + namespace JSX { + interface IntrinsicElements { + 'my-component': JSX.HTMLAttributes; + } + } +} + +export function Component(): VNode { + return + + ; +} diff --git a/examples/external/package.json b/examples/external/package.json index b0ea3660..5ded0c0d 100644 --- a/examples/external/package.json +++ b/examples/external/package.json @@ -1,5 +1,6 @@ { "name": "rules_prerender_external", "version": "0.0.0", - "private": true + "private": true, + "type": "module" } diff --git a/examples/external/site.mts b/examples/external/site.mts deleted file mode 100644 index 505cfd6b..00000000 --- a/examples/external/site.mts +++ /dev/null @@ -1,25 +0,0 @@ -import { PrerenderResource, unsafeTreatStringAsSafeHtml } from 'rules_prerender'; -import { renderComponent } from './component/component.mjs'; - -export default function*(): Generator { - // TODO: Migrate to Preact once we figure out how to handle - // `@rules_prerender/preact`'s peer dep in an external repository with - // manual `npm_link_package()` dependencies. - yield PrerenderResource.fromHtml( - '/index.html', - unsafeTreatStringAsSafeHtml(` - - - - Test - - - -

Hello, World!

- - ${renderComponent()} - - - `.trim()), - ); -} diff --git a/examples/external/site.tsx b/examples/external/site.tsx new file mode 100644 index 00000000..69c88bf8 --- /dev/null +++ b/examples/external/site.tsx @@ -0,0 +1,18 @@ +import { PrerenderResource, renderToHtml } from '@rules_prerender/preact'; +import { Component } from './component/component.js'; + +export default function*(): Generator { + yield PrerenderResource.fromHtml('/index.html', renderToHtml( + + + Test + + + +

Hello, World!

+ + + + + )); +} diff --git a/examples/external/tsconfig.json b/examples/external/tsconfig.json index a467a361..2e396110 100644 --- a/examples/external/tsconfig.json +++ b/examples/external/tsconfig.json @@ -6,9 +6,11 @@ "moduleResolution": "node", "declaration": true, "sourceMap": true, + "jsx": "react-jsx", + "jsxImportSource": "preact", "paths": { "rules_prerender": ["../../packages/rules_prerender"], - "@rules_prerender/declarative_shadow_dom": ["../../packages/declarative_shadow_dom"], + "@rules_prerender/*": ["../../packages/*"], }, } } diff --git a/packages/preact/BUILD.bazel b/packages/preact/BUILD.bazel index 27e8318f..b232d15a 100644 --- a/packages/preact/BUILD.bazel +++ b/packages/preact/BUILD.bazel @@ -34,7 +34,10 @@ ts_project( deps = [ ":node_modules/preact-render-to-string", "//:node_modules/preact", - "//:node_modules/rules_prerender", + # As a peer dep, we can't depend on the JS implementation of + # `rules_prerender` because it needs to be supplied at link time. + # See `//:node_modules/@rules_prerender/preact` deps. + "//:node_modules_types/rules_prerender", ], )