From d0ce7f1f89e770afda63a54d0992b0e33e1ae427 Mon Sep 17 00:00:00 2001 From: Tom Pereira Date: Tue, 24 Dec 2024 17:31:32 +0000 Subject: [PATCH 01/19] change initial commit message in changelogs (#3) --- docs/CHANGELOG.md | 2 +- packages/features/docs/CHANGELOG.md | 2 +- packages/react-pointcuts/docs/CHANGELOG.md | 2 +- packages/ssr/docs/CHANGELOG.md | 2 +- packages/webpack/docs/CHANGELOG.md | 3 +-- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index fe59d05..6f9d606 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -233,4 +233,4 @@ This log covers the [monorepo](https://en.wikipedia.org/wiki/Monorepo). ### Added -- Initial version, copying code from PLP. +- Initial commit. diff --git a/packages/features/docs/CHANGELOG.md b/packages/features/docs/CHANGELOG.md index be37a9f..abc3dd8 100644 --- a/packages/features/docs/CHANGELOG.md +++ b/packages/features/docs/CHANGELOG.md @@ -93,4 +93,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Initial version, copying code from PLP +- Initial commit. diff --git a/packages/react-pointcuts/docs/CHANGELOG.md b/packages/react-pointcuts/docs/CHANGELOG.md index 10c985a..932854d 100644 --- a/packages/react-pointcuts/docs/CHANGELOG.md +++ b/packages/react-pointcuts/docs/CHANGELOG.md @@ -103,4 +103,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Initial version, copying code from PLP +- Initial commit. diff --git a/packages/ssr/docs/CHANGELOG.md b/packages/ssr/docs/CHANGELOG.md index f7b0824..75ca4ea 100644 --- a/packages/ssr/docs/CHANGELOG.md +++ b/packages/ssr/docs/CHANGELOG.md @@ -83,4 +83,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Initial version, copying code from PLP +- Initial commit. diff --git a/packages/webpack/docs/CHANGELOG.md b/packages/webpack/docs/CHANGELOG.md index ae2c30c..5102394 100644 --- a/packages/webpack/docs/CHANGELOG.md +++ b/packages/webpack/docs/CHANGELOG.md @@ -103,5 +103,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Initial version, copying code from PLP -- Added unit tests around the plugin itself, not originally developed in PLP +- Initial commit From 27e436a05e74b57ec5de78cb88fe09594c55c70e Mon Sep 17 00:00:00 2001 From: Tom Pereira <10725179+TomStrepsil@users.noreply.github.com> Date: Thu, 30 Jan 2025 15:30:26 +0000 Subject: [PATCH 02/19] [#18] Fix JSDoc module names (#19) * rename to proper module namespace * update docs links * update versions * web toggle point in readme title * fixup changelog from revised 0.x range * 2.0.0 -> 0.5.0 in oss version scheme * fix broken link syntax in CHANGELOG * consistent quoting * more version history issues * fixup module name in jsdoc * add web remove sdkInstanceProvider * remove SDKInstanceProvider * fixup jsdoc dedupe * tweak * clarity re: ssr package * casing etc --- docs/CHANGELOG.md | 17 +++++++++----- docs/README.md | 2 +- docs/dedupeExternalJsdocPlugin.js | 10 ++++----- package-lock.json | 12 +++++----- package.json | 2 +- packages/features/docs/CHANGELOG.md | 10 +++++++-- packages/features/docs/README.md | 2 +- packages/features/package.json | 2 +- packages/features/src/global.js | 14 ++++++------ packages/features/src/global/store.js | 10 ++++----- .../features/src/nodeRequestScoped/store.js | 10 ++++----- packages/features/src/reactContext/store.js | 10 ++++----- .../src/ssrBackedReactContext/store.js | 12 +++++----- packages/react-pointcuts/docs/CHANGELOG.md | 8 ++++++- packages/react-pointcuts/docs/README.md | 2 +- packages/react-pointcuts/package.json | 2 +- packages/react-pointcuts/src/external.js | 11 ---------- .../src/getCodeSelectionPlugins.js | 2 +- packages/react-pointcuts/src/index.js | 2 +- .../src/withTogglePointFactory/index.js | 8 +++---- .../src/withToggledHookFactory/index.js | 8 +++---- packages/ssr/docs/CHANGELOG.md | 8 ++++++- packages/ssr/docs/README.md | 2 +- packages/ssr/package.json | 2 +- packages/ssr/src/external.js | 11 ---------- packages/ssr/src/index.js | 4 ++-- .../ssr/src/serializationFactory/index.js | 22 +++++++++---------- packages/ssr/src/withJsonIsomorphism/index.js | 2 +- packages/webpack/docs/CHANGELOG.md | 6 +++++ packages/webpack/docs/README.md | 2 +- packages/webpack/package.json | 2 +- packages/webpack/src/index.js | 2 +- .../src/plugins/togglePointInjection/index.js | 2 +- 33 files changed, 115 insertions(+), 106 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6f9d606..532c7d8 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -14,6 +14,13 @@ N.B. See changelogs for individual packages, where most change will occur: This log covers the [monorepo](https://en.wikipedia.org/wiki/Monorepo). +## [0.10.2] - 2024-12-26 + +### Fixed + +- "Toggle Point" to "Web Toggle Point" in title of `README.md` +- fixed the dedupe external JSDoc plugin, after move to type imports + ## [0.10.1] - 2024-12-24 ### Fixed @@ -73,7 +80,7 @@ This log covers the [monorepo](https://en.wikipedia.org/wiki/Monorepo). ### Fixed -- Removed old `yarn.lock` left over from 1.0.3 update. +- Removed old `yarn.lock` left over from 0.4.3 update. ## Added @@ -85,7 +92,7 @@ This log covers the [monorepo](https://en.wikipedia.org/wiki/Monorepo). - Moved to v4 of [`upload-artifact`](https://github.com/actions/upload-artifact) and [`download-artifact`](https://github.com/actions/download-artifact) actions - Changed nature of pre-release packages to `beta` from `alpha` (better matching the reality of how these pre-releases are used) -- Fixed up contribution guide since version 2.0.0 added the proposed update checks +- Fixed up contribution guide since version 0.5.0 added the proposed update checks - Updated to JSDoc 4, issue with factories resolved ### Changed @@ -127,7 +134,7 @@ This log covers the [monorepo](https://en.wikipedia.org/wiki/Monorepo). ### Fixed -- Fixup documentation left fallow from package split (2.0.0) +- Fixup documentation left fallow from package split (0.5.0) - Upgrade serialize-javascript to 6.0.2 to avoid [`SNYK-JS-SERIALIZEJAVASCRIPT-614760`](https://security.snyk.io/vuln/SNYK-JS-SERIALIZEJAVASCRIPT-6147607) - snyk ignore [`SNYK-JS-INFLIGHT-6095116`](https://security.snyk.io/vuln/SNYK-JS-INFLIGHT-6095116) - move to use asos runner groups @@ -157,7 +164,7 @@ This log covers the [monorepo](https://en.wikipedia.org/wiki/Monorepo). ### Changed - Split the "app" package into separate "ssr", "features" and "react-pointcuts" packages. -- Move to explicit rather than wildcard workspaces, to enable reification of the repo when publishing (waiting on [https://github.com/Roaders/workspace-version/issues/3](an issue to resolve)) +- Move to explicit rather than wildcard workspaces, to enable reification of the repo when publishing (waiting on [an issue to resolve](https://github.com/Roaders/workspace-version/issues/3)) - Updated the `dedupeExternalJsdocPlugin` to de-duplicate members of external namespaces, rather than just the namespaces themselves (to ensure we don't duplicate React, HostApplication etc. in the html documentation) - Updated packages for snyk vulnerabilities, populated policy file - Removed redundant export fields from workspace `package.json` @@ -175,7 +182,7 @@ This log covers the [monorepo](https://en.wikipedia.org/wiki/Monorepo). ### Added -- Danger support, with checks for CHANGELOG.md updates and package-lock.json updates +- Danger support, with checks for `CHANGELOG.md` updates and `package-lock.json` updates ### Fixed diff --git a/docs/README.md b/docs/README.md index 116c4fd..e6e59fa 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,5 @@

-

Toggle Point

+

Web Toggle Point

A library providing a means to toggle or branch web application code. diff --git a/docs/dedupeExternalJsdocPlugin.js b/docs/dedupeExternalJsdocPlugin.js index e339a52..56ee304 100644 --- a/docs/dedupeExternalJsdocPlugin.js +++ b/docs/dedupeExternalJsdocPlugin.js @@ -18,15 +18,15 @@ exports.defineTags = function (dictionary) { .synonym("external"); }; -const seenExternals = new Map(); +const seen = new Map(); exports.handlers = { jsdocCommentFound: function (e) { if (e.filename.endsWith("external.js")) { - const match = e.comment.match(/external:(\S+)/); + const match = e.comment.match(/(?:[\s\S]*@typedef \{.+\} (?.+))?[\s\S]+external:(?\S+)/); if (match) { - const [external] = match; - if (!seenExternals.has(external)) { - seenExternals.set(external, true); + const symbol = match.groups.typedef || match.groups.external; + if (!seen.has(symbol)) { + seen.set(symbol, true); } else { e.comment = "/**/"; } diff --git a/package-lock.json b/package-lock.json index e3b0abd..71ede54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@asos/web-toggle-point", - "version": "0.10.1", + "version": "0.10.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@asos/web-toggle-point", - "version": "0.10.1", + "version": "0.10.2", "license": "MIT", "workspaces": [ "packages/features", @@ -20346,7 +20346,7 @@ }, "packages/features": { "name": "@asos/web-toggle-point-features", - "version": "0.3.0", + "version": "0.3.1", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0" @@ -20403,7 +20403,7 @@ }, "packages/react-pointcuts": { "name": "@asos/web-toggle-point-react-pointcuts", - "version": "0.4.0", + "version": "0.4.2", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0" @@ -20441,7 +20441,7 @@ }, "packages/ssr": { "name": "@asos/web-toggle-point-ssr", - "version": "0.2.0", + "version": "0.2.1", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", @@ -20477,7 +20477,7 @@ }, "packages/webpack": { "name": "@asos/web-toggle-point-webpack", - "version": "0.7.2", + "version": "0.7.3", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", diff --git a/package.json b/package.json index 5c5e40e..d72f35a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@asos/web-toggle-point", - "version": "0.10.1", + "version": "0.10.2", "repository": "git@github.com:asos/web-toggle-point.git", "homepage": "https://asos.github.io/web-toggle-point/", "license": "MIT", diff --git a/packages/features/docs/CHANGELOG.md b/packages/features/docs/CHANGELOG.md index abc3dd8..fa7b6a2 100644 --- a/packages/features/docs/CHANGELOG.md +++ b/packages/features/docs/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.1] - 2024-12-26 + +### Fixed + +- updated some errant JSDoc namespaces + ## [0.3.0] - 2024-11-28 ### Changed @@ -32,7 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - updated to latest `@testing-library/react` to remove errant warning about import of `act` - updated to `react@18.3.1`, set minimum required react to `17` - - technically a breaking change, but `jsx-runtime` already introduced in [version 1.0.0](#100---2023-09-12)... so was already broken, oops. + - technically a breaking change, but `jsx-runtime` already introduced in [version 0.1.0](#010---2023-09-12)... so was already broken, oops. - renamed commonJs exports to have `.cjs` extension to prevent `[ERR_REQUIRE_ESM]` errors in consumers that aren't `"type": "module"` ## [0.2.2] - 2024-12-26 @@ -61,7 +67,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- Fixup documentation left fallow from package split (0.1.0) +- Fixup documentation left fallow from package split ([version 0.1.0](#010---2023-09-12)) ## [0.1.1] - 2023-11-16 diff --git a/packages/features/docs/README.md b/packages/features/docs/README.md index 0353466..fd7cdef 100644 --- a/packages/features/docs/README.md +++ b/packages/features/docs/README.md @@ -8,7 +8,7 @@ A store should be chosen based on the requirement for global or partitioned stat ## Usage -See: [JSDoc output](https://asos.github.io/web-toggle-point/module-asos-web-toggle-point-features.html) +See: [JSDoc output](https://asos.github.io/web-toggle-point/module-web-toggle-point-features.html) > [!WARNING] > ### Use with React 17 diff --git a/packages/features/package.json b/packages/features/package.json index 36abbcf..e38f533 100644 --- a/packages/features/package.json +++ b/packages/features/package.json @@ -1,7 +1,7 @@ { "name": "@asos/web-toggle-point-features", "description": "toggle point features code", - "version": "0.3.0", + "version": "0.3.1", "license": "MIT", "type": "module", "main": "./lib/main.es5.cjs", diff --git a/packages/features/src/global.js b/packages/features/src/global.js index abf4590..bd618e8 100644 --- a/packages/features/src/global.js +++ b/packages/features/src/global.js @@ -2,21 +2,21 @@ import "./external"; /** * Application code for holding feature toggle state - * @module toggle-point-features + * @module web-toggle-point-features */ /** * Interface for feature toggle stores * * @interface FeaturesStore - * @memberof module:toggle-point-features + * @memberof module:web-toggle-point-features */ /** * Method to return the value of the feature toggle store. * For {@link https://reactjs.org/docs/context.html|React context}-backed feature stores, this should be called following {@link https://react.dev/warnings/invalid-hook-call-warning|the rules of hooks} * * @function - * @memberof module:toggle-point-features + * @memberof module:web-toggle-point-features * @name FeaturesStore#getFeatures */ @@ -24,13 +24,13 @@ import "./external"; * Interface for singleton value-based feature toggle stores * * @interface SingletonFeaturesStore - * @memberof module:toggle-point-features + * @memberof module:web-toggle-point-features */ /** * Method to set a value to the feature toggle store. * * @function - * @memberof module:toggle-point-features + * @memberof module:web-toggle-point-features * @name SingletonFeaturesStore#useValue * @param {object} params parameters * @param {object} params.value A value to store, used to drive feature toggles. @@ -40,13 +40,13 @@ import "./external"; * Interface for {@link https://reactjs.org/docs/context.html|React context}-based feature toggle stores * * @interface ContextFeaturesStore - * @memberof module:toggle-point-features + * @memberof module:web-toggle-point-features */ /** * Method to create a React context provider, linked to the features store. * * @function - * @memberof module:toggle-point-features + * @memberof module:web-toggle-point-features * @name ContextFeaturesStore#providerFactory * @returns {external:React.Component} A react context provider that accepts a `value` prop, representing the feature toggle state. */ diff --git a/packages/features/src/global/store.js b/packages/features/src/global/store.js index 7722bd3..2a0c8da 100644 --- a/packages/features/src/global/store.js +++ b/packages/features/src/global/store.js @@ -8,16 +8,16 @@ const storeMap = new WeakMap(); * A thin wrapper around a singleton, used as an extension point for future plugins. * Consider {@link https://github.com/christophehurpeau/deep-freeze-es6|deep freezing} the value to prevent accidental mutation, if this is intended to be static. * For reactive decisions, consider implementing something that allows for reactivity e.g. a {@link https://github.com/pmndrs/valtio|valtio/vanilla} proxy, and subscribe appropriately in a toggle point. - * @memberof module:toggle-point-features - * @returns {module:toggle-point-features.globalFeaturesStore} A store for features, held globally in the application. + * @memberof module:web-toggle-point-features + * @returns {module:web-toggle-point-features.globalFeaturesStore} A store for features, held globally in the application. */ const globalFeaturesStoreFactory = () => { const identifier = Symbol(); /** * @name globalFeaturesStore - * @memberof module:toggle-point-features - * @implements module:toggle-point-features.FeaturesStore - * @implements module:toggle-point-features.SingletonFeaturesStore + * @memberof module:web-toggle-point-features + * @implements module:web-toggle-point-features.FeaturesStore + * @implements module:web-toggle-point-features.SingletonFeaturesStore */ return { useValue: ({ value }) => { diff --git a/packages/features/src/nodeRequestScoped/store.js b/packages/features/src/nodeRequestScoped/store.js index 0092fb4..c2232df 100644 --- a/packages/features/src/nodeRequestScoped/store.js +++ b/packages/features/src/nodeRequestScoped/store.js @@ -4,17 +4,17 @@ import { AsyncLocalStorage } from "async_hooks"; * A factory function used to create a store for features, held in request-scoped global value. * Should only be used server-side, for anything user or request specific. * A thin wrapper around node {@link https://nodejs.org/api/async_context.html#class-asynclocalstorage|AsyncLocalStorage}, used as an extension point for future plugins. - * @memberof module:toggle-point-features - * @returns {module:toggle-point-features.requestScopedFeaturesStore} A store for features, scoped for the current request. + * @memberof module:web-toggle-point-features + * @returns {module:web-toggle-point-features.requestScopedFeaturesStore} A store for features, scoped for the current request. */ const requestScopedFeaturesStoreFactory = () => { const store = new AsyncLocalStorage(); /** * @name requestScopedFeaturesStore - * @memberof module:toggle-point-features - * @implements module:toggle-point-features.FeaturesStore - * @implements module:toggle-point-features.SingletonFeaturesStore + * @memberof module:web-toggle-point-features + * @implements module:web-toggle-point-features.FeaturesStore + * @implements module:web-toggle-point-features.SingletonFeaturesStore */ return { useValue: ({ value, scopeCallBack }) => { diff --git a/packages/features/src/reactContext/store.js b/packages/features/src/reactContext/store.js index db64c55..6ef45cb 100644 --- a/packages/features/src/reactContext/store.js +++ b/packages/features/src/reactContext/store.js @@ -4,17 +4,17 @@ import PropTypes from "prop-types"; /** * A factory function used to create a store for features, held in a {@link https://reactjs.org/docs/context.html|React context}. * A thin wrapper around a context, used as an extension point for future plugins. - * @memberof module:toggle-point-features - * @returns {module:toggle-point-features.reactContextFeaturesStore} A store for features, held within a {@link https://reactjs.org/docs/context.html|React context}. + * @memberof module:web-toggle-point-features + * @returns {module:web-toggle-point-features.reactContextFeaturesStore} A store for features, held within a {@link https://reactjs.org/docs/context.html|React context}. */ const reactContextFeaturesStoreFactory = ({ name }) => { const context = createContext(); /** * @name reactContextFeaturesStore - * @memberof module:toggle-point-features - * @implements module:toggle-point-features.FeaturesStore - * @implements module:toggle-point-features.ContextFeaturesStore + * @memberof module:web-toggle-point-features + * @implements module:web-toggle-point-features.FeaturesStore + * @implements module:web-toggle-point-features.ContextFeaturesStore */ return { providerFactory: () => { diff --git a/packages/features/src/ssrBackedReactContext/store.js b/packages/features/src/ssrBackedReactContext/store.js index ea91d06..44b624f 100644 --- a/packages/features/src/ssrBackedReactContext/store.js +++ b/packages/features/src/ssrBackedReactContext/store.js @@ -3,9 +3,9 @@ import reactContextFeaturesStoreFactory from "../reactContext/store"; /** * A factory function used to create a store for features, held in a {@link https://reactjs.org/docs/context.html|React context}, backed by server-side rendering. - * A wrapper around a {@link module:toggle-point-features.reactContextFeaturesStore|reactContextFeaturesStore}, with server-side rendering supplied by the {@link module:toggle-point-ssr|toggle-point-ssr} package. - * @memberof module:toggle-point-features - * @returns {module:toggle-point-features.ssrBackedReactContextFeaturesStore} A store for features, held within a {@link https://reactjs.org/docs/context.html|React context}. + * A wrapper around a {@link module:web-toggle-point-features.reactContextFeaturesStore|reactContextFeaturesStore}, with server-side rendering supplied by the {@link module:web-toggle-point-ssr|toggle-point-ssr} package. + * @memberof module:web-toggle-point-features + * @returns {module:web-toggle-point-features.ssrBackedReactContextFeaturesStore} A store for features, held within a {@link https://reactjs.org/docs/context.html|React context}. */ const ssrBackedReactContextFeaturesStoreFactory = ({ namespace = "toggles", @@ -18,9 +18,9 @@ const ssrBackedReactContextFeaturesStoreFactory = ({ /** * @name ssrBackedReactContextFeaturesStore - * @memberof module:toggle-point-features - * @implements module:toggle-point-features.FeaturesStore - * @implements module:toggle-point-features.ContextFeaturesStore + * @memberof module:web-toggle-point-features + * @implements module:web-toggle-point-features.FeaturesStore + * @implements module:web-toggle-point-features.ContextFeaturesStore */ return { providerFactory: () => { diff --git a/packages/react-pointcuts/docs/CHANGELOG.md b/packages/react-pointcuts/docs/CHANGELOG.md index 932854d..cd6be4f 100644 --- a/packages/react-pointcuts/docs/CHANGELOG.md +++ b/packages/react-pointcuts/docs/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.4.2] - 2024-12-26 + +### Fixed + +- updated some errant JSDoc namespaces + ## [0.4.1] - 2024-12-17 ### Removed @@ -33,7 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - a `Map` of features (de-coupling from a webpack-specific data structure) - a [javascript module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules), rather than its `default` export (preparing for support of named exports) - updated to `react@18.3.1`, set minimum required react to `17` - - technically a breaking change, but `jsx-runtime` already introduced in [version 1.0.0](#100---2023-09-05)... so was already broken, oops. + - technically a breaking change, but `jsx-runtime` already introduced in [version 0.1.0](#010---2023-09-05)... so was already broken, oops. - moved package to `"type": "module"` and renamed commonJs exports to have `.cjs` extension to prevent `[ERR_REQUIRE_ESM]` errors in consumers that aren't `"type": "module"` ### Fixed diff --git a/packages/react-pointcuts/docs/README.md b/packages/react-pointcuts/docs/README.md index 8fb4694..92347dc 100644 --- a/packages/react-pointcuts/docs/README.md +++ b/packages/react-pointcuts/docs/README.md @@ -18,7 +18,7 @@ Both accept plugins, currently supporting a hook called during code activation ( ## Usage -See: [JSDoc output](https://asos.github.io/web-toggle-point/module-asos-web-toggle-point-react-pointcuts.html) +See: [JSDoc output](https://asos.github.io/web-toggle-point/module-web-toggle-point-react-pointcuts.html) > [!WARNING] > ### Use with React 17 diff --git a/packages/react-pointcuts/package.json b/packages/react-pointcuts/package.json index 62282e7..7ade338 100644 --- a/packages/react-pointcuts/package.json +++ b/packages/react-pointcuts/package.json @@ -1,7 +1,7 @@ { "name": "@asos/web-toggle-point-react-pointcuts", "description": "react pointcut code", - "version": "0.4.0", + "version": "0.4.2", "license": "MIT", "type": "module", "main": "./lib/main.es5.cjs", diff --git a/packages/react-pointcuts/src/external.js b/packages/react-pointcuts/src/external.js index ef62d00..d3834a4 100644 --- a/packages/react-pointcuts/src/external.js +++ b/packages/react-pointcuts/src/external.js @@ -2,17 +2,6 @@ * Code expected in the host application * @external HostApplication */ -/** - * A factory for SDKs; should return an instance of asos-web-features when called with "features" - * @callback external:HostApplication.sdkInstanceProvider - * @async - * @type {Function} - * @param {string} sdkName Name of the SDK to access; will be passed "features" - * @returns {external:asos-web-features} - * @see SiteChrome SDK interface {@link https://asoscom.atlassian.net/wiki/spaces/WEB/pages/593592455/SCP+-+Interface+Definition#SDK-Instances|SDK Instances} - * @example - * const sdkInstance = await sdkInstanceProvider("features"); - */ /** * A delegate passed to log an error * @callback external:HostApplication.logError diff --git a/packages/react-pointcuts/src/getCodeSelectionPlugins.js b/packages/react-pointcuts/src/getCodeSelectionPlugins.js index 7dde7b2..4aa9b83 100644 --- a/packages/react-pointcuts/src/getCodeSelectionPlugins.js +++ b/packages/react-pointcuts/src/getCodeSelectionPlugins.js @@ -1,6 +1,6 @@ /** * A plugin for the point cuts package - * @typedef {object} module:toggle-point-react-pointcuts~plugin + * @typedef {object} module:web-toggle-point-react-pointcuts~plugin * @property {string} name plugin name, used as a prefix when creating {@link https://legacy.reactjs.org/docs/higher-order-components.html|React Higher-Order-Components} when toggling * @property {Function} onCodeSelected hook to be called when a code selection is made */ diff --git a/packages/react-pointcuts/src/index.js b/packages/react-pointcuts/src/index.js index fc91576..b31c5df 100644 --- a/packages/react-pointcuts/src/index.js +++ b/packages/react-pointcuts/src/index.js @@ -4,6 +4,6 @@ import "./external"; /** * Application code for creating a React {@link https://en.wikipedia.org/wiki/Pointcut|pointcut}. - * @module toggle-point-react-pointcuts + * @module web-toggle-point-react-pointcuts */ export { withTogglePointFactory, withToggledHookFactory }; diff --git a/packages/react-pointcuts/src/withTogglePointFactory/index.js b/packages/react-pointcuts/src/withTogglePointFactory/index.js index cd8e6f0..ecd92a2 100644 --- a/packages/react-pointcuts/src/withTogglePointFactory/index.js +++ b/packages/react-pointcuts/src/withTogglePointFactory/index.js @@ -7,17 +7,17 @@ import getCodeSelectionPlugins from "../getCodeSelectionPlugins"; {} /** * A factory function used to create a withTogglePoint React Higher-Order-Component. - * @memberof module:toggle-point-react-pointcuts + * @memberof module:web-toggle-point-react-pointcuts * @inner * @function * @param {object} params parameters * @param {function} params.getActiveFeatures a method to get active features. Called honouring the rules of hooks. * @param {external:HostApplication.logError} params.logError a method that logs errors * @param {string} [params.variantKey=bucket] A key use to identify a variant from the features data structure. Remaining members of the feature will be passed to the variant as props. - * @param {Array} [params.plugins] plugins to be used when toggling + * @param {Array} [params.plugins] plugins to be used when toggling * Will be used when a toggled component throws an error that can be caught by an {@link https://reactjs.org/docs/error-boundaries.html|ErrorBoundary}. * When errors are caught, the control/base code will be used as the fallback component. - * @returns {module:toggle-point-react-pointcuts.withTogglePoint} withTogglePoint React Higher-Order-Component. + * @returns {module:web-toggle-point-react-pointcuts.withTogglePoint} withTogglePoint React Higher-Order-Component. * @example * const withTogglePoint = withTogglePointFactory({ * getActiveFeatures, @@ -37,7 +37,7 @@ const withTogglePointFactory = ({ /** * A React Higher-Order-Component that wraps a base / control component and swaps in a variant when deemed appropriate by a context * @function withTogglePoint - * @memberof module:toggle-point-react-pointcuts + * @memberof module:web-toggle-point-react-pointcuts * @param {ReactComponentModuleNamespaceObject} controlModule The control / base module * @param {external:React.Component} controlModule.default The control react component * @param {Map} featuresMap A map of features and their variants, with features as top-level keys and variants as nested keys with modules as the values. diff --git a/packages/react-pointcuts/src/withToggledHookFactory/index.js b/packages/react-pointcuts/src/withToggledHookFactory/index.js index 61baeb7..13c7413 100644 --- a/packages/react-pointcuts/src/withToggledHookFactory/index.js +++ b/packages/react-pointcuts/src/withToggledHookFactory/index.js @@ -6,13 +6,13 @@ import getCodeSelectionPlugins from "../getCodeSelectionPlugins"; {} /** * A factory function used to create a withToggledHook React hook, wrapping an existing hook/function. - * @memberof module:toggle-point-react-pointcuts + * @memberof module:web-toggle-point-react-pointcuts * @inner * @function * @param {object} params parameters * @param {function} params.getActiveFeatures a method to get active features, which is called honouring the rules of hooks. - * @param {Array} [params.plugins] plugins to be used when toggling - * @returns {module:toggle-point-react-pointcuts.withToggledHook} withToggledHook hook function, use to wrap a function (either a hook itself, or a function that must be called wherever a hook can...). + * @param {Array} [params.plugins] plugins to be used when toggling + * @returns {module:web-toggle-point-react-pointcuts.withToggledHook} withToggledHook hook function, use to wrap a function (either a hook itself, or a function that must be called wherever a hook can...). * @example * const getActiveFeatures = () => useContext(myContext); * const withToggledHook = withToggledHookFactory({ @@ -28,7 +28,7 @@ const withToggledHookFactory = ({ getActiveFeatures, plugins = [] }) => { /** * A React hook that wraps a base / control function or hook and swaps in a variant when deemed appropriate by a context * @function withToggledHook - * @memberof module:toggle-point-react-pointcuts + * @memberof module:web-toggle-point-react-pointcuts * @param {ReactHookModuleNamespaceObject} controlModule The control / base module * @param {(external:React.Hook|function)} controlModule.default The control react hook or function. * @param {Map} featuresMap A map of features and their variants, with features as top-level keys and variants as nested keys with modules as the values. diff --git a/packages/ssr/docs/CHANGELOG.md b/packages/ssr/docs/CHANGELOG.md index 75ca4ea..31ed972 100644 --- a/packages/ssr/docs/CHANGELOG.md +++ b/packages/ssr/docs/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.1] - 2024-12-26 + +### Fixed + +- updated some errant JSDoc namespaces + ## [0.2.0] - 2024-12-17 ### Removed @@ -50,7 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - updated to latest `@testing-library/react` to remove errant warning about import of `act` - updated to `react@18.3.1`, set minimum required react to `17` - - technically a breaking change, but `jsx-runtime` already introduced in [version 1.0.0](#100---2023-09-05)... so was already broken, oops. + - technically a breaking change, but `jsx-runtime` already introduced in [version 0.1.0](#010---2023-09-05)... so was already broken, oops. - renamed commonJs exports to have `.cjs` extension to prevent `[ERR_REQUIRE_ESM]` errors in consumers that aren't `"type": "module"` ## [0.1.2] - 2024-12-06 diff --git a/packages/ssr/docs/README.md b/packages/ssr/docs/README.md index 78845c2..54873d8 100644 --- a/packages/ssr/docs/README.md +++ b/packages/ssr/docs/README.md @@ -28,7 +28,7 @@ For the browser: ## Usage -See: [JSDoc output](https://asos.github.io/web-toggle-point/module-asos-web-toggle-point-ssr.html) +See: [JSDoc output](https://asos.github.io/web-toggle-point/module-web-toggle-point-ssr.html) > [!WARNING] > ### Use with React 17 diff --git a/packages/ssr/package.json b/packages/ssr/package.json index 2a6578d..10b2ff7 100644 --- a/packages/ssr/package.json +++ b/packages/ssr/package.json @@ -1,7 +1,7 @@ { "name": "@asos/web-toggle-point-ssr", "description": "toggle point server side rendering code", - "version": "0.2.0", + "version": "0.2.1", "license": "MIT", "type": "module", "main": "./lib/main.es5.cjs", diff --git a/packages/ssr/src/external.js b/packages/ssr/src/external.js index 3603746..68c0153 100644 --- a/packages/ssr/src/external.js +++ b/packages/ssr/src/external.js @@ -2,17 +2,6 @@ * Code expected in the host application * @external HostApplication */ -/** - * A factory for SDKs; should return an instance of asos-web-features when called with "features" - * @callback external:HostApplication.sdkInstanceProvider - * @async - * @type {Function} - * @param {string} sdkName Name of the SDK to access; will be passed "features" - * @returns {external:asos-web-features} - * @see SiteChrome SDK interface {@link https://asoscom.atlassian.net/wiki/spaces/WEB/pages/593592455/SCP+-+Interface+Definition#SDK-Instances|SDK Instances} - * @example - * const sdkInstance = await sdkInstanceProvider("features"); - */ /** * A delegate passed to log a warning * @callback external:HostApplication.logWarning diff --git a/packages/ssr/src/index.js b/packages/ssr/src/index.js index cbba6c8..e4d6750 100644 --- a/packages/ssr/src/index.js +++ b/packages/ssr/src/index.js @@ -3,7 +3,7 @@ import withJsonIsomorphism from "./withJsonIsomorphism"; import "./external"; /** - * Server Side Rendering code for Isomorphic React applications - * @module asos-web-toggle-point-ssr + * Server Side Rendering code for isomorphic / universal applications + * @module web-toggle-point-ssr */ export { withJsonIsomorphism, serializationFactory }; diff --git a/packages/ssr/src/serializationFactory/index.js b/packages/ssr/src/serializationFactory/index.js index 53ab300..c3e6794 100644 --- a/packages/ssr/src/serializationFactory/index.js +++ b/packages/ssr/src/serializationFactory/index.js @@ -5,16 +5,16 @@ import parse from "html-react-parser"; {} /** * A factory for creating a serialization object that has methods for serializing and deserializing JSON data in server-rendered web applications. - * @memberof module:asos-web-toggle-point-ssr + * @memberof module:web-toggle-point-ssr * @inner * @function - * @memberof module:asos-web-toggle-point-ssr + * @memberof module:web-toggle-point-ssr * @inner * @function * @param {object} params parameters * @param {string} params.id The id attribute of the backing application/json script. * @param {external:HostApplication.logWarning} params.logWarning A method that logs warnings; will be used when malformed JSON is found in the backing store when deserialize on the client, which should only be possible if processed in a system downstream from the origin. - * @returns {module:asos-web-toggle-point-ssr.serialization} Some serialization / deserialization methods + * @returns {module:web-toggle-point-ssr.serialization} Some serialization / deserialization methods * @example * const logWarning = (warning) => console.log(warning); * const id = "app_features"; @@ -23,33 +23,33 @@ import parse from "html-react-parser"; const serializationFactory = ({ id, logWarning }) => /** * @typedef {function} getScriptMarkup - * @memberof module:asos-web-toggle-point-ssr + * @memberof module:web-toggle-point-ssr * @param {object} params parameters * @param {object} params.content The JSON content to be serialized. */ /** * @typedef {function} getScriptReactElement - * @memberof module:asos-web-toggle-point-ssr + * @memberof module:web-toggle-point-ssr * @param {object} params parameters * @param {object} params.content The JSON content to be serialized. */ /** * @typedef {function} getJSONFromScript - * @memberof module:asos-web-toggle-point-ssr + * @memberof module:web-toggle-point-ssr * @returns {object} The JSON content of the script element. */ /** * An object containing methods for serializing and deserializing JSON data in server-rendered web applications. * @typedef {object} serialization - * @memberof module:asos-web-toggle-point-ssr - * @property {module:asos-web-toggle-point-ssr.getScriptMarkup} getScriptMarkup Gets a string containing markup for a type="application/json" script element with the specified content. - * @property {module:asos-web-toggle-point-ssr.getScriptReactElement} getScriptReactElement - Gets a React element for a type="application/json" script element with the specified content. - * @property {module:asos-web-toggle-point-ssr.getJSONFromScript} getJSONFromScript - Returns the JSON content of the script element. + * @memberof module:web-toggle-point-ssr + * @property {module:web-toggle-point-ssr.getScriptMarkup} getScriptMarkup Gets a string containing markup for a type="application/json" script element with the specified content. + * @property {module:web-toggle-point-ssr.getScriptReactElement} getScriptReactElement - Gets a React element for a type="application/json" script element with the specified content. + * @property {module:web-toggle-point-ssr.getJSONFromScript} getJSONFromScript - Returns the JSON content of the script element. */ ({ /** - * @memberof module:asos-web-toggle-point-ssr.serialization + * @memberof module:web-toggle-point-ssr.serialization * @param {object} content The JSON content to be serialized. * @returns {string} A string containing markup for a type="application/json" script element with the specified content. */ diff --git a/packages/ssr/src/withJsonIsomorphism/index.js b/packages/ssr/src/withJsonIsomorphism/index.js index b68c691..8559f37 100644 --- a/packages/ssr/src/withJsonIsomorphism/index.js +++ b/packages/ssr/src/withJsonIsomorphism/index.js @@ -8,7 +8,7 @@ import { useState, useEffect } from "react"; * which are then realised into a prop when first hydrating on the client. It will be reactive to subsequent non-`undefined` prop values, * for that prop. * The package "browser" export includes the code to read the script, omitted from the "import" / "require" export (server package). - * @memberof module:asos-web-toggle-point-ssr + * @memberof module:web-toggle-point-ssr * @inner * @function * @param {external:React.Component} WrappedComponent The React component that will receive the props. diff --git a/packages/webpack/docs/CHANGELOG.md b/packages/webpack/docs/CHANGELOG.md index 5102394..9467746 100644 --- a/packages/webpack/docs/CHANGELOG.md +++ b/packages/webpack/docs/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.7.3] - 2024-12-26 + +### Fixed + +- updated some errant JSDoc namespaces + ## [0.7.2] - 2024-12-17 ### Removed diff --git a/packages/webpack/docs/README.md b/packages/webpack/docs/README.md index 823c534..f4c7b1d 100644 --- a/packages/webpack/docs/README.md +++ b/packages/webpack/docs/README.md @@ -20,7 +20,7 @@ The join points are configured using a [glob](https://en.wikipedia.org/wiki/Glob ### Configuration -See [JSDoc output](https://asos.github.io/web-toggle-point/module-asos-web-toggle-point-webpack.html) +See [JSDoc output](https://asos.github.io/web-toggle-point/module-web-toggle-point-webpack.html) Different code paths may have different toggling needs, and may want a toggle point applied in differing ways. Independent point cuts should be configured for each different: diff --git a/packages/webpack/package.json b/packages/webpack/package.json index 38fbc45..ec17f81 100644 --- a/packages/webpack/package.json +++ b/packages/webpack/package.json @@ -1,7 +1,7 @@ { "name": "@asos/web-toggle-point-webpack", "description": "toggle point webpack plugin", - "version": "0.7.2", + "version": "0.7.3", "license": "MIT", "type": "module", "main": "./lib/main.cjs", diff --git a/packages/webpack/src/index.js b/packages/webpack/src/index.js index a99fcdc..a11c1e9 100644 --- a/packages/webpack/src/index.js +++ b/packages/webpack/src/index.js @@ -2,6 +2,6 @@ import "./external.js"; /** * Webpack code for injecting toggle points - * @module toggle-point-webpack + * @module web-toggle-point-webpack */ export { TogglePointInjection } from "./plugins"; diff --git a/packages/webpack/src/plugins/togglePointInjection/index.js b/packages/webpack/src/plugins/togglePointInjection/index.js index df98970..0148828 100644 --- a/packages/webpack/src/plugins/togglePointInjection/index.js +++ b/packages/webpack/src/plugins/togglePointInjection/index.js @@ -9,7 +9,7 @@ import schema from "./schema.json"; /** * Toggle Point Injection Plugin - * @memberof module:toggle-point-webpack + * @memberof module:web-toggle-point-webpack * @inner */ class TogglePointInjection { From 1f0cb29d36b158f19c1301eba2c122c0810c146b Mon Sep 17 00:00:00 2001 From: Tom Pereira <10725179+TomStrepsil@users.noreply.github.com> Date: Thu, 6 Mar 2025 12:18:52 +0000 Subject: [PATCH 03/19] [26] Fix public/scoped package publishing (#27) * update workflows * version * typo * update chromium linux snaps * versions for serve update * package.json repository field * update root package.lock * bugs & directories/doc fields * fix changelog --------- Co-authored-by: Tom Pereira --- .github/actions/publish/publish.sh | 2 +- .github/workflows/release.yaml | 2 ++ docs/CHANGELOG.md | 6 ++++++ examples/serve/docs/CHANGELOG.md | 7 +++++++ examples/serve/package.json | 2 +- .../-screenshots-control-chromium-linux.png | Bin 1515 -> 1281 bytes ...enshots-st-patricks-day-chromium-linux.png | Bin 3354 -> 3079 bytes package-lock.json | 14 +++++++------- package.json | 2 +- packages/features/docs/CHANGELOG.md | 6 ++++++ packages/features/package.json | 13 ++++++++++++- packages/react-pointcuts/docs/CHANGELOG.md | 6 ++++++ packages/react-pointcuts/package.json | 13 ++++++++++++- packages/ssr/docs/CHANGELOG.md | 6 ++++++ packages/ssr/package.json | 13 ++++++++++++- packages/webpack/docs/CHANGELOG.md | 6 ++++++ packages/webpack/package.json | 13 ++++++++++++- 17 files changed, 97 insertions(+), 14 deletions(-) diff --git a/.github/actions/publish/publish.sh b/.github/actions/publish/publish.sh index cd06cdf..cd8920a 100755 --- a/.github/actions/publish/publish.sh +++ b/.github/actions/publish/publish.sh @@ -1,2 +1,2 @@ TAG=$([ "$PRE_RELEASE" == "true" ] && echo "--tag=pre-release ") -npm publish $TAG--workspace=$WORKSPACE 2> publish_stderr_digest.log +npm publish --access public $TAG--workspace=$WORKSPACE 2> publish_stderr_digest.log diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index eb97d40..ef7b596 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -219,3 +219,5 @@ jobs: name: ${{ matrix.package.name }} version: ${{ steps.newVersion.outputs.version }} is-pre-release: ${{ env.IS_PRE_RELEASE }} + env: + NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 532c7d8..42e9399 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -14,6 +14,12 @@ N.B. See changelogs for individual packages, where most change will occur: This log covers the [monorepo](https://en.wikipedia.org/wiki/Monorepo). +## [0.10.3] - 2025-02-27 + +### Fixed + +- GHA pipelines for publishing to public/scoped NPM repository + ## [0.10.2] - 2024-12-26 ### Fixed diff --git a/examples/serve/docs/CHANGELOG.md b/examples/serve/docs/CHANGELOG.md index 126a3c8..9f2b321 100644 --- a/examples/serve/docs/CHANGELOG.md +++ b/examples/serve/docs/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.3] - 2025-02-27 + +### Changed + +- updated some linux playwright snapshots + - no code changes, so this must be a change in linux chromium. Assets look identical to eye, so presumably need to relax the fuzziness. + ## [0.2.2] - 2024-12-17 ### Removed diff --git a/examples/serve/package.json b/examples/serve/package.json index 945644b..20a3f5d 100644 --- a/examples/serve/package.json +++ b/examples/serve/package.json @@ -1,6 +1,6 @@ { "name": "web-toggle-point-serve-example", - "version": "0.2.0", + "version": "0.2.3", "type": "module", "private": true, "scripts": { diff --git a/examples/serve/src/fixtures/event/playwright.spec.ts-snapshots/-screenshots-control-chromium-linux.png b/examples/serve/src/fixtures/event/playwright.spec.ts-snapshots/-screenshots-control-chromium-linux.png index 6615aa1a81dce531b97b8f2d111a015ab6a08e19..f1aa0ebed6c0d579a89a296353976c95a38abd46 100644 GIT binary patch delta 1249 zcmV<71RneA3xNudFn)my&Yb+>UG`%YU6`?^f*U%c*q61R@!uK()RUrEOE zp4{I7pq}kA#OdlH001CSx2CS*gd+OMdUO6JcDvo8#RP%DFF zbljMgS+zGGRDX&b%W_hAuu78Epe%8P0w+~Qo8*RjZM2FWr8wAZIz_%RPfjND3a|Ep zy8&lqR_!fjjgr6?3Y<`C&i1ESHigqcg~swc>zA#25mHW(O@Xs@$!N|Nog!D6-x6oi zFeIKWu$*Kx=fTaAuKMzf6d9BRyWMUVj10cOtCh}Vv44q@vywk12G5r|^G%c_AInii z;-uy>IYwuBwJ{H(D45kGRwxI98%o2Ih&efQQObVUE^7qWsS+H z&}d??>fmryE^55g`8ujsTlXWRCjF)Yw}dTR4}ZFY{yb)wXMf`jQD5at2ZMSO^ zPnnZHjbqUdfsT(0DK;iS`qZVm4!o(0NgIQ3c+VIEAZqfdP>3L;^y-y39Gh~>0{{Sk zEq}*r$(P%=BiyeLe_MfnZV4-1y+z%6>sV=NpU!|`4ke}c<)IYmhkEG#{^dEuT7B#Q z4fn@uA_8zN{rM3-b25j=7YnVs9-GXC4}?$Jm<080k7{7*V$wzcgm;Y{02t;x?sX{u z07S;VPU+1mKuUzPq~B72+IFh2k6<44<$s|R*_3<(vD+o}>GpgPBE(Qik*uJ#?Vf#Q z<2&|NRK!Pz+Jh7Yt=%<)J^^ufSw)%+HVJa3u4lX0G|`g*Q(++7yFvK0QLd1NbR#p_G1RU3>dv2NG=FOXT?%bg|^jDJRdhu{3 z003sQxvsA6>VMU%008iKy!Q6?l9G}$XU=4F!Zd#oD2nR!`ttJf-Q8U@hRw~*_V#v> zNW|fAsNF;K*-2`3Y`gpzG!-o%VZ*OBh4Gj%Xo;)E*2!dcqNr}Z` zSzTQ<8jUO#%j5B&D7v$=)6~?|)zuY=LK9u2d?0^X84y=@bfuo12?>_@hUU8XFs#p21yNs@#znGCn*=jX4luM=Lxfqy{Y)TvWgyQQV&?%lfthqxKYSCN_mafN;G z-~nMicDwz;g$o=G2e)AP6YfhwDaaRM#sD91ai!1^=?Wy`8+zi4!LZ3JNS1%f!TlR;x`iAqiPoS$JSh zPEH0_K{Yn6uyMoZ=H@mwHe&7R?0-uJl*FELrKP3A!^43<06~zKFJDeiPv5w4Ln4t> zRaITPcFpVcuC1+&jEp>f{J5*DE6LQw#l@E{UD9YYU%!4027|M+vllO3G#CuY`{;DK zTeof%78VKwf+Q1WoOFur6&ow;z`#Ikcv)H5*|TSBYiqxK`xXj?j7DQoQGZb=6iRbn zGN2@O$9eVYl}sk%a=BbCx1yrLVzFTMt*xz&jt;R{oSmI5lgU1Q{D@zXKoI=#;|JC@ z8jTed75I=+sl+Z*csQHQmPjP{2hnIqe0)A1004u7g9)9ZCxfDBV`JmPhYtzYw^#@l z;k_~>QY16DxJI9zHa0fe+S)`S5u42}FE9V}=@X@WA(0{-B^mTT_h_g> n|6oZ&8T!!BP=-DuSX(O1u+tD@ zm(@Y&RzWYTjGbEFVzVc&FX_s;vjXnY_%H#E5Z-uU{D#rR%+{;Xea-wCU_j%VA` zwo`Gv%X6t;alM<3=C={vL^@H>)T4UdyYv0CdqO3DL&)EX`nYrFqq&{9MPawq+AY-B zESuig_dv9#cc5>mI)8rYmc3Um@8$a2apg_sQl??PJAV@Zd~{IY*iPAJ+9U zu7}85^pU~G-m6aB!}rHHetxlb_L6_8wlF}ph#Nn*`rTMDReWZ&24HaCUbDDIreNNr zq5tbMuYU(ypWKrW+6noi1m=vV(-R&*5?V2}^5flLLj=l$r{Y~PsKi78yfCcY=3v?( zs>@EgDeQI%>A3TKj-Laf;UXJ9Ti7Wa*hoIUz9+m`Ti|=5+xj>EUx0l(zEhd|WhBxw zK!*Bz<4t|}u3Kw-lJ(s(1pp!pXG;5R`GlMn?|+sd&z~+99G8VeR?=F`vptUxN&rel z+WziLrDQlRs;)CNHiaEzJncYGl}sYOs*OrHUZi&i9Wg-w7^N%y#~debjGIoC5vu4) zIw^Kk0qqcV8=Q1g*zFWjPb3o77I{PgZ0_xU|AU`=dh1{QyYpA!;z9(tcf+T)fBe%w z{C~yQYxMwi#Q1xkRQ{u>FCmfKl6(5xEHIYNCmc1Kk!1?dkxYRIQp+&>>0*AoWXIFR zJ+e1lwi%Iz6p;X-S9VI4$An>B7q2u!zCDr8+otU^5J4G9XB4$bl2@`E(!>+eW7GPZ z8UU0{<TM5?1xRkg@Cd z=7g1PI0$h80P|gsh|5c=Neo@&Cv+{lSe&$3Oid~Ri2~e>h2)#Bs`^_@-D?=b)IO0) z9V?m#%EggZwRDq2b(`(w=%%pS*|mGc{_gQ#K6n4%1_0aoH}^z(rrCTye@|uZSAU@5}Ai?bkBpm?BoKu~~e6uVwC$N$3<_)KXJS z-DegRnfRsDQQZ;>)vZlN2%~9ahTtLQ#0h)#cL4#tDq;%D;`$E&Jd( zf~3o=ls|Jii0(M7myIDaapVt2H$VG7Pi2~p6s)-1rkpl3cXwWLGRC>700W=>l#$<` zlu4_A#V+yVPaDZ%W^|73+9DC(KFP>n%~uZirIF>;>?Ea^kC9#f=_FJFfPWRVBZtQN zpZHO#n20q?B6Hg(s%en7%=YaO_A0w}i}l5~&W>*S*b<4+NKd`KcqyEHXVzIe!J{MLwFds*U^<^rMHq zH-*IGwv#UBV&a{ivFw@Gq za>@JFdq00P;h#FB7IJp$N9zhdtdx53>Cri|@5{FRHLLth=S^j0LE01M40u_Tmy}Tz z5__M2Ue%cAvZhHSZ4=cZ$oAR(P=&opYv*_;7JIx6eSG8V`QU>G)^FO_H+11QTy$L! z>G3wK;}_~Rf8i2<>VL%rg!gT~=QFK+va0J;96y#Y^c1S4Wd5;Z=bq6DzLm-ePQj{y z- zW}e7eAOOJ0k8YzhWWcgvwUuRDk)wv~xt3RQdCichYc@9~lz#%?wbbs$oF{6-Qf9^x z1qq?e0K^)L41&;SA)$oQ7^NIo$oxJ#bxtxT{LmM+r~cb}j*HYU1dZ7Q>j`)M-FI7fV}3DJNFJLF5PkGt z(j@?hB{Irqi^op}?-ufIE>raZh=SZmBO#Omv$L)j0vux@2w9?t5DM^=(rYVYKu}}= z!^+@Wr>U4LB~%IkZrO2(Aj>z+Ks$;xod0yFa}V1WwAjB2Y}KTX&G=g_*ptDqBM5(<^+wBSvCgA!wP?T z%$;^1D*zDM`I1qlN#hZfwMZh`B&ua<+GV>Hx-0C}Tf3cGzPOQmqF%29k-^v}AKv!u zFMZ;>y?@dET77|V8y?;H&oc{)?=4(v7)PT$y?e3qvwR%Oti< zltbxxE=yFX*r4{aY74C)MJ&NYWF)jCm=!KVC^Uxzh*?(sRkSar56r*swQvNl!H0y|*B;80kU2Ze~ zB?N)%dw>K0%&~p&Lz+rj#^85@pLjwa%$}UFO~=JYX3T1wf?cNxTW7l&2m`KK|fsV7npUE(`nS-?eMNIR4d#9{BRZ+kd|T zU~ay0YUY*d{Dp0s)FJv%G_p!@dXq-_HKQ~$6)gS04-6=}nm1&L00`aUtI704*{^{P zDBA99ZjUMe%(u%Gk8zSJu5iHOd6r%AJm!OH`f#^SU>vh-`9ebP?094e%o$JWmgRGz zB4u;hiYJ@PY@Ui2zB%sEp>$z?LVo~r%6j62&EjeEV7i$s&^>EQSn-te`mTHp9~PKzl_p(KMH`kV;5r4HCK9XfgstT2qn5;ug>KzpA)UD} zp8u~W4}bB+jq8Peeq#q7*06@V5FHCUn$2cAxZkG!``%}j@4xX4u7{U|-|xV~8rE#=KC!$$(03H$#6xPes0BF2(|00000NkvXXu0mjf DQGgC^ delta 3339 zcmV+m4fOJd7@8W8Fn-0yzBd+#^j+yT++_0Wet^r2@!&AUxQD1R;ONgw*~_F;Bn59&Zj}_CYeRQ3cNTqk7eVNWCV7)R%N_ybZTr&e=wV`aduvzM`jGs=py96l zm`-*~YZi@4Q-42e{?l|LBAKYDuBh-uVM$|2i>ie@S<+ZS=!x$7^UuSv;$ssJO$-%; z-mSa)c0Ium?5Dz?@HCwL9XtJI`rCD;x6#AIHfd|8(J94>DlWqlL#Z}&7pYoAo~P6P zx}AozO)+(?>e|vXOUV>rQCKW0OC%H7&};x8%p)vkPJfOmM*i8D;5A{^u~|NLJ|i4Q zylr1#{DARkm(zUv`6dS?zinSE4+|!C*M)So{MT#3?~+jSbz!lM~vdDX~%g{K#09-G;$Y+ezv!W57H zUikUe&wnQ$pCoj|mgp_R9EO>~v7%bBs4O2lpEvI8Yv5+>1^`nAP3i4{OcBbmMl6`v zCKXcNV*yT+0bp94@>caeKc|nlbbBUBC{p@Qg6U<2QhUnty_`PtK|c)x44OoUNSi)ZVdY+G}(g zFRm92qnT)=xS{yudmlX}L1eW7XNm zYk#yl4Fcd~C@Am(9#J}VH;xPpK z(C0%2&M@xAjpsKG+&kcE?YcT_b>WM`dTD*QN4Pc3T1}`kOERCld@_E(_z>3+13$Yo zTTmpZmQ-^Y-06d-FY;f6BCmfT?8pps34h#_zv)`lwGB}l0ASVmRX3mC%$$|!Yw!D7 zvt>{xncX@qA)K}|t|UqXYtA-go2firUAbALkJLKzzv zgY5=m+kaK3xYn;K8$(dNUN6^(Yg-=cbozp7fmS8tQJJc6Z%GULeXp|k5{_3@eUF$ZJF-d(k%I!qAud)eupmC&V0#iQFF<{7dk#{|w`&^XsH_ zZ8_wsvF498GltBNuYZvXrV94o-5;G1ttQk)e6WZ)@ z`XXRYz`2TZQTwB=&%b`6J|zB(}zsYxSz2$Z*78K0syE8)#m}9FZN#y z0OJOX%PGrQ61ZeZ;1a_op6vcrw^Rdwhpor=e}0dnID#M;g2j5rDm03Ao)}?jlKRVE!5$zB?x_^a$>*wQbul2 zM>~%OfPZLy^v3*+QT?NKP2L3nYC@e`o;$*EM1R}%ukXg0fi8iWC7HKg+=}y!qheGe zWHq4%0B@c*9e=fek?$K({iDb=l1*i6by@^LevJ9Cqb#M3rENJFf+@91!_*9uwWZq{ zcev;JWR+%-d_#AEe0L=$*cQRA!Ga>e1JMKWP=BdaF8qCAf7|{k5h+H3NXlZ0P!>@e zL$JH7=XcZ0e1_Pl<(L~4T zL9RgzoMFV_p6lal@7pe9Ib}Iy`=QipUhDK9)Q#k-!czWTBkT0khb=Txw34RHAm3b1C zBw<*>?4h$ASPrlC#|SfqACqz^#ofj|%p=S=naAX1&B`)#_SJ+s#4RMmEkr054tE$% z#i+Ml+**HSeWkdP!{Bs`tEa7JTYruYbaIXSnfMubRiRasH52(wxeK2c4;<>pk(g01&hKu|H=8qxr+n$1lrJSm=xyGxTBK>P9nc& zbQZ*J3AH79%iQ5}Ev|MiC3d&}*N(*2KN9>sCXe-+0{}90E~KXpMsi#aRJv28|94>mBh`)t?kv$g4lDRxW7SfTz zb!tMH7`)kxdr2s?irUs%1VQYX_J(bq0a~3_5Bk?et`EHpp;&mj{C_l`%a7(qBYyk$FIYD25Qpi=qH)Zy(1$*}gD^X>v6Pe)Gsfex zoF&ReL4Goh(tI4pm|>w0eRv09c4C|PL;?UUp?=a(=*b&F@8o^14}JJM!o0-(AFhOd V$k~JKp#T5?07*qoL Date: Thu, 6 Mar 2025 14:23:30 +0000 Subject: [PATCH 04/19] fix registry url etc (#34) Co-authored-by: Tom Pereira --- .github/actions/publish/action.yaml | 2 ++ docs/CHANGELOG.md | 6 ++++++ package.json | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/actions/publish/action.yaml b/.github/actions/publish/action.yaml index bb3659c..2106e92 100644 --- a/.github/actions/publish/action.yaml +++ b/.github/actions/publish/action.yaml @@ -21,6 +21,8 @@ runs: - uses: actions/setup-node@v4 with: node-version-file: .nvmrc + registry-url: https://registry.npmjs.org/ + always-auth: true cache: npm - name: Install diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 42e9399..bfcd51b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -14,6 +14,12 @@ N.B. See changelogs for individual packages, where most change will occur: This log covers the [monorepo](https://en.wikipedia.org/wiki/Monorepo). +## [0.10.4] - 2025-03-06 + +### Fixed + +- Ensured that the registry is explicitly set, to ensure that [`@actions/setup-node`](https://github.com/actions/setup-node) honours the `NODE_AUTH_TOKEN` + ## [0.10.3] - 2025-02-27 ### Fixed diff --git a/package.json b/package.json index a2992c6..540f361 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@asos/web-toggle-point", - "version": "0.10.3", + "version": "0.10.4", "repository": "git@github.com:asos/web-toggle-point.git", "homepage": "https://asos.github.io/web-toggle-point/", "license": "MIT", From e942cde472bd47b769bfeea78d4b702b4d3a4088 Mon Sep 17 00:00:00 2001 From: TomStrepsil <10725179+TomStrepsil@users.noreply.github.com> Date: Tue, 29 Jul 2025 21:01:45 +0100 Subject: [PATCH 05/19] Extract portions of load strategies update and apply separately --- docs/CHANGELOG.md | 7 + eslint.config.mjs | 10 +- examples/express/docs/CHANGELOG.md | 6 + examples/express/package.json | 2 +- .../express/src/routes/animals/middleware.js | 1 - examples/next/docs/CHANGELOG.md | 6 + examples/next/next.config.mjs | 4 +- examples/serve/docs/CHANGELOG.md | 8 +- .../listExtractionFromPathSegment.js | 6 +- .../singleFilenameDottedSegment.js | 6 +- .../src/toggleHandlers/singlePathSegment.js | 6 +- package-lock.json | 53 +--- package.json | 4 +- packages/webpack/docs/CHANGELOG.md | 15 ++ packages/webpack/package.json | 13 +- .../fillDefaultOptionalValues.js | 27 ++ .../fillDefaultOptionalValues.test.js | 84 ++++++ .../src/plugins/togglePointInjection/index.js | 17 +- .../togglePointInjection/index.test.js | 241 +++++++++--------- .../togglePointInjection/integration.test.js | 6 +- .../plugins/togglePointInjection/logger.js | 8 +- .../togglePointInjection/logger.test.js | 13 +- .../fillDefaultOptionalValues.js | 13 - .../fillDefaultOptionalValues.test.js | 74 ------ .../processPointCuts/index.js | 5 +- .../processPointCuts/index.test.js | 20 +- .../processVariantFiles/index.js | 18 +- .../processVariantFiles/index.test.js | 128 +++++----- .../processVariantFiles/linkJoinPoints.js | 13 + .../linkJoinPoints.test.js | 109 ++++++++ .../resolveJoinPoints/index.js | 55 ++-- .../resolveJoinPoints/index.test.js | 21 +- .../setupSchemeModules/generateJoinPoint.js | 20 -- .../generateJoinPoint.test.js | 47 ---- .../generateJoinPoint/createVariantPathMap.js | 5 + .../createVariantPathMap.test.js | 11 + .../generateJoinPoint/importCodeGenerator.js | 15 ++ .../importCodeGenerator.test.js | 44 ++++ .../generateJoinPoint/index.js | 18 ++ .../generateJoinPoint/index.test.js | 51 ++++ .../setupSchemeModules/generatePointCut.js | 4 +- .../generatePointCut.test.js | 6 +- .../setupSchemeModules/index.js | 8 +- .../setupSchemeModules/index.test.js | 21 +- .../pathSegmentToggleHandler.js | 18 +- .../pathSegmentToggleHandler.test.js | 30 ++- packages/webpack/test/test-utils.js | 1 - 47 files changed, 770 insertions(+), 528 deletions(-) create mode 100644 packages/webpack/src/plugins/togglePointInjection/fillDefaultOptionalValues.js create mode 100644 packages/webpack/src/plugins/togglePointInjection/fillDefaultOptionalValues.test.js delete mode 100644 packages/webpack/src/plugins/togglePointInjection/processPointCuts/fillDefaultOptionalValues.js delete mode 100644 packages/webpack/src/plugins/togglePointInjection/processPointCuts/fillDefaultOptionalValues.test.js create mode 100644 packages/webpack/src/plugins/togglePointInjection/processPointCuts/processVariantFiles/linkJoinPoints.js create mode 100644 packages/webpack/src/plugins/togglePointInjection/processPointCuts/processVariantFiles/linkJoinPoints.test.js delete mode 100644 packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint.js delete mode 100644 packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint.test.js create mode 100644 packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/createVariantPathMap.js create mode 100644 packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/createVariantPathMap.test.js create mode 100644 packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/importCodeGenerator.js create mode 100644 packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/importCodeGenerator.test.js create mode 100644 packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/index.js create mode 100644 packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/index.test.js diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index af73a0d..9ce5f67 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -14,6 +14,13 @@ N.B. See changelogs for individual packages, where most change will occur: This log covers the [monorepo](https://en.wikipedia.org/wiki/Monorepo). +## [0.10.7] - 2025-07-29 + +### Changed + +- updated to `0.11.0` of [`eslint-plugin-workspaces`](https://github.com/joshuajaco/eslint-plugin-workspaces) after [addition of ESLint9 support](https://github.com/joshuajaco/eslint-plugin-workspaces/commit/af855c3a3d8069366d4446747e91828ddf7560c6) + - update `eslint.config.mjs` to utilise flat config + ## [0.10.6] - 2025-07-14 ### Fixed diff --git a/eslint.config.mjs b/eslint.config.mjs index 93f947b..3ffc16a 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -2,19 +2,13 @@ import asosConfig from "./peripheral/eslint-config-asosconfig/index.js"; import globals from "globals"; import jsdoc from "eslint-plugin-jsdoc"; import markdown from "@eslint/markdown"; -import { FlatCompat } from "@eslint/eslintrc"; -import path from "path"; -import { fileURLToPath } from "url"; +import workspaces from "eslint-plugin-workspaces"; const scripts = ["*.{js,mjs}", "**/*.{js,mjs}"]; const markDowns = ["*.md", "**/*.md"]; -const compat = new FlatCompat({ - baseDirectory: path.dirname(fileURLToPath(import.meta.url)) -}); - export default [ - ...compat.extends("plugin:workspaces/recommended"), + workspaces.configs["flat/recommended"], ...asosConfig.map((config) => ({ files: scripts, ignores: ["**/docs/**", "**/danger/**"], diff --git a/examples/express/docs/CHANGELOG.md b/examples/express/docs/CHANGELOG.md index af7bab2..03a6019 100644 --- a/examples/express/docs/CHANGELOG.md +++ b/examples/express/docs/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.6] - 2025-07-29 + +### Fixed + +- removed "Vary" header from "animals" example, the page is meant to be un-cacheable, and the value was wrong in any case + ## [0.2.5] - 2025-05-27 ### Changed diff --git a/examples/express/package.json b/examples/express/package.json index 07a30e8..4894627 100644 --- a/examples/express/package.json +++ b/examples/express/package.json @@ -1,6 +1,6 @@ { "name": "web-toggle-point-express-example", - "version": "0.2.5", + "version": "0.2.6", "type": "module", "engines": { "node": ">=20.6.0" diff --git a/examples/express/src/routes/animals/middleware.js b/examples/express/src/routes/animals/middleware.js index ef8eae2..ea8e1b7 100644 --- a/examples/express/src/routes/animals/middleware.js +++ b/examples/express/src/routes/animals/middleware.js @@ -8,7 +8,6 @@ const contextMiddleware = (request, response, scopeCallBack) => { response.status(StatusCodes.BAD_REQUEST).end(); return; } - response.header("Vary", version); featuresStore.useValue({ value: { version }, scopeCallBack }); }; diff --git a/examples/next/docs/CHANGELOG.md b/examples/next/docs/CHANGELOG.md index 91c3ed8..e918e3c 100644 --- a/examples/next/docs/CHANGELOG.md +++ b/examples/next/docs/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.5] - 2025-05-29 + +### Changed + +- update to take supply static `webpackNormalModule` corresponding to webpack plugin [version 0.9.0](../../../packages/webpack/docs/CHANGELOG.md#090---2025-05-29) + ## [0.2.4] - 2025-05-27 ### Changed diff --git a/examples/next/next.config.mjs b/examples/next/next.config.mjs index 428a031..799b10b 100644 --- a/examples/next/next.config.mjs +++ b/examples/next/next.config.mjs @@ -1,5 +1,6 @@ import createMDX from "@next/mdx"; import remarkGfm from "remark-gfm"; +import webpackNormalModule from "next/dist/compiled/webpack/NormalModule.js"; /** @type {import('next').NextConfig} */ const nextConfig = { @@ -17,8 +18,7 @@ const togglePointInjection = new TogglePointInjection({ ] } ], - webpackNormalModule: async () => - (await import("next/dist/compiled/webpack/NormalModule.js")).default + webpackNormalModule }); nextConfig.webpack = (config) => { diff --git a/examples/serve/docs/CHANGELOG.md b/examples/serve/docs/CHANGELOG.md index b3f8ea1..e2ca06f 100644 --- a/examples/serve/docs/CHANGELOG.md +++ b/examples/serve/docs/CHANGELOG.md @@ -5,11 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.2.7] - 2025-07-29 + +### Changed + +- updated toggle handlers to take a `variantPathMap` corresponding to webpack [version 0.9.0](../../../packages/webpack/docs/CHANGELOG.md#090---2025-05-29) + ## [0.2.6] - 2025-07-14 ### Changed -- updated to use `variantGlobs` array, with updated webpack plugin [0.8.0][version 0.8.0](../../../packages/webpack/docs/CHANGELOG.md#080---2025-05-27) +- updated to use `variantGlobs` array, with updated webpack plugin [version 0.8.0](../../../packages/webpack/docs/CHANGELOG.md#080---2025-05-27) ## [0.2.5] - 2025-07-14 diff --git a/examples/serve/src/toggleHandlers/listExtractionFromPathSegment.js b/examples/serve/src/toggleHandlers/listExtractionFromPathSegment.js index 6fc9234..e8189b7 100644 --- a/examples/serve/src/toggleHandlers/listExtractionFromPathSegment.js +++ b/examples/serve/src/toggleHandlers/listExtractionFromPathSegment.js @@ -1,9 +1,9 @@ -export default ({ togglePoint, joinPoint, variants }) => { - const featuresMap = variants.keys().reduce((map, key) => { +export default ({ togglePoint, joinPoint, variantPathMap }) => { + const featuresMap = variantPathMap.keys().reduce((map, key) => { const [, , value] = key.split("/"); const list = value.split(","); for (const value of list) { - map.set(value, variants(key)); + map.set(value, variantPathMap.get(key)); } return map; }, new Map()); diff --git a/examples/serve/src/toggleHandlers/singleFilenameDottedSegment.js b/examples/serve/src/toggleHandlers/singleFilenameDottedSegment.js index d9dc3bc..2d9a6a3 100644 --- a/examples/serve/src/toggleHandlers/singleFilenameDottedSegment.js +++ b/examples/serve/src/toggleHandlers/singleFilenameDottedSegment.js @@ -1,8 +1,8 @@ -export default ({ togglePoint, joinPoint, variants }) => { +export default ({ togglePoint, joinPoint, variantPathMap }) => { const featuresMap = new Map(); - for (const key of variants.keys()) { + for (const key of variantPathMap.keys()) { const [, , value] = key.split("."); - featuresMap.set(value, variants(key)); + featuresMap.set(value, variantPathMap.get(key)); } return togglePoint(joinPoint, featuresMap); }; diff --git a/examples/serve/src/toggleHandlers/singlePathSegment.js b/examples/serve/src/toggleHandlers/singlePathSegment.js index 9cf4f2d..7a72925 100644 --- a/examples/serve/src/toggleHandlers/singlePathSegment.js +++ b/examples/serve/src/toggleHandlers/singlePathSegment.js @@ -1,8 +1,8 @@ -export default ({ togglePoint, joinPoint, variants }) => { +export default ({ togglePoint, joinPoint, variantPathMap }) => { const featuresMap = new Map(); - for (const key of variants.keys()) { + for (const key of variantPathMap.keys()) { const [, value] = key.split("/"); - featuresMap.set(value, variants(key)); + featuresMap.set(value, variantPathMap.get(key)); } return togglePoint(joinPoint, featuresMap); }; diff --git a/package-lock.json b/package-lock.json index df4b789..3929385 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@asos/web-toggle-point", - "version": "0.10.6", + "version": "0.10.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@asos/web-toggle-point", - "version": "0.10.6", + "version": "0.10.7", "license": "MIT", "workspaces": [ "packages/features", @@ -42,7 +42,7 @@ "eslint-plugin-jest-formatting": "^3.1.0", "eslint-plugin-jsdoc": "^50.5.0", "eslint-plugin-prettier": "^5.2.1", - "eslint-plugin-workspaces": "^0.10.1", + "eslint-plugin-workspaces": "^0.11.0", "globals": "^15.12.0", "jsdoc": "^4.0.4", "lint-staged": "^15.2.10", @@ -57,7 +57,7 @@ }, "examples/express": { "name": "web-toggle-point-express-example", - "version": "0.2.5", + "version": "0.2.6", "dependencies": { "@asos/web-toggle-point-features": "file:../../packages/features", "@asos/web-toggle-point-react-pointcuts": "file:../../packages/react-pointcuts", @@ -6048,7 +6048,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001690", + "version": "1.0.30001731", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz", + "integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==", "funding": [ { "type": "opencollective", @@ -8518,11 +8520,13 @@ } }, "node_modules/eslint-plugin-workspaces": { - "version": "0.10.1", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-workspaces/-/eslint-plugin-workspaces-0.11.0.tgz", + "integrity": "sha512-1Ol5QoV+IDBt/YiGCAXWKccKI3AAUSQUmaz0cw0at/MjgEPHvCQAkrv5U2p0C3YInd4sOfBzmyumhWFl6n6INQ==", "dev": true, "license": "MIT", "dependencies": { - "find-workspaces": "^0.3.0" + "find-workspaces": "^0.3.1" } }, "node_modules/eslint-scope": { @@ -16576,30 +16580,6 @@ "@babel/runtime": "^7.8.4" } }, - "node_modules/regexgen": { - "version": "1.3.0", - "license": "MIT", - "dependencies": { - "jsesc": "^2.3.0", - "regenerate": "^1.3.2" - }, - "bin": { - "regexgen": "bin/cli.js" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/regexgen/node_modules/jsesc": { - "version": "2.5.2", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/regexp.prototype.flags": { "version": "1.5.3", "license": "MIT", @@ -19936,12 +19916,11 @@ }, "packages/webpack": { "name": "@asos/web-toggle-point-webpack", - "version": "0.8.0", + "version": "0.9.0", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "fast-glob": "^3.2.12", - "regexgen": "^1.3.0" + "fast-glob": "^3.2.12" }, "devDependencies": { "@rollup/plugin-babel": "^6.0.2", @@ -19963,13 +19942,7 @@ "webpack-test-utils": "^2.1.0" }, "peerDependencies": { - "next": ">14", "webpack": ">=5.70" - }, - "peerDependenciesMeta": { - "next": { - "optional": true - } } }, "peripheral/babel-preset-asos": { diff --git a/package.json b/package.json index c59b22c..1bd3fc1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@asos/web-toggle-point", - "version": "0.10.6", + "version": "0.10.7", "repository": "git@github.com:asos/web-toggle-point.git", "homepage": "https://asos.github.io/web-toggle-point/", "license": "MIT", @@ -51,7 +51,7 @@ "eslint-plugin-jest-formatting": "^3.1.0", "eslint-plugin-jsdoc": "^50.5.0", "eslint-plugin-prettier": "^5.2.1", - "eslint-plugin-workspaces": "^0.10.1", + "eslint-plugin-workspaces": "^0.11.0", "globals": "^15.12.0", "jsdoc": "^4.0.4", "lint-staged": "^15.2.10", diff --git a/packages/webpack/docs/CHANGELOG.md b/packages/webpack/docs/CHANGELOG.md index 8673a3c..66419da 100644 --- a/packages/webpack/docs/CHANGELOG.md +++ b/packages/webpack/docs/CHANGELOG.md @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.9.0] - 2025-07-29 + +### Changed + +- consolidated setting of default optional values +- changed `variants` array on join point data structure to a `Map` of relative to absolute path as `variantPathMap` +- move away from webpack `import.meta.webpackContext` when generating join points, construct a `Map` manually instead + - add linking of join points, to supplant the functionality previously provided by `import.meta.webpackContext` +- updated win32 path replacement, can effectively no-op on posix systems + +### Fixed + +- removed "next" peer dependency, this needn't be explicit +- ensured files that cannot be resolved (by [enhanced-resolve](https://github.com/webpack/enhanced-resolve/)), for whatever reason, don't break the build + ## [0.8.0] - 2025-05-27 ### Changed diff --git a/packages/webpack/package.json b/packages/webpack/package.json index 093b158..02afc72 100644 --- a/packages/webpack/package.json +++ b/packages/webpack/package.json @@ -1,7 +1,7 @@ { "name": "@asos/web-toggle-point-webpack", "description": "toggle point webpack plugin", - "version": "0.8.0", + "version": "0.9.0", "license": "MIT", "type": "module", "main": "./lib/main.cjs", @@ -35,8 +35,7 @@ }, "dependencies": { "@babel/runtime": "^7.26.0", - "fast-glob": "^3.2.12", - "regexgen": "^1.3.0" + "fast-glob": "^3.2.12" }, "devDependencies": { "@rollup/plugin-babel": "^6.0.2", @@ -58,12 +57,6 @@ "webpack-test-utils": "^2.1.0" }, "peerDependencies": { - "webpack": ">=5.70", - "next": ">14" - }, - "peerDependenciesMeta": { - "next": { - "optional": true - } + "webpack": ">=5.70" } } diff --git a/packages/webpack/src/plugins/togglePointInjection/fillDefaultOptionalValues.js b/packages/webpack/src/plugins/togglePointInjection/fillDefaultOptionalValues.js new file mode 100644 index 0000000..a23fbbe --- /dev/null +++ b/packages/webpack/src/plugins/togglePointInjection/fillDefaultOptionalValues.js @@ -0,0 +1,27 @@ +import { posix, basename } from "path"; +import webpack from "webpack"; + +const fillDefaultPointcutValues = (pointCut) => { + const { + variantGlobs = ["./**/__variants__/*/*/!(*.test).{js,jsx,ts,tsx}"], + joinPointResolver = (variantPath) => + posix.resolve(variantPath, ...Array(4).fill(".."), basename(variantPath)), + toggleHandler = "@asos/web-toggle-point-webpack/pathSegmentToggleHandler" + } = pointCut; + return { + ...pointCut, + variantGlobs, + joinPointResolver, + toggleHandler + }; +}; + +const fillDefaultOptionalValues = (options) => { + return { + webpackNormalModule: webpack.NormalModule, + ...options, + pointCuts: options.pointCuts.map(fillDefaultPointcutValues) + }; +}; + +export default fillDefaultOptionalValues; diff --git a/packages/webpack/src/plugins/togglePointInjection/fillDefaultOptionalValues.test.js b/packages/webpack/src/plugins/togglePointInjection/fillDefaultOptionalValues.test.js new file mode 100644 index 0000000..7e5a2f4 --- /dev/null +++ b/packages/webpack/src/plugins/togglePointInjection/fillDefaultOptionalValues.test.js @@ -0,0 +1,84 @@ +import webpack from "webpack"; +import fillDefaultOptionalValues from "./fillDefaultOptionalValues.js"; + +jest.mock("webpack", () => ({ NormalModule: Symbol("test-normal-module") })); + +describe("fillDefaultOptionalValues", () => { + let result; + + const makeDefaultJoinPointResolverAssertions = () => { + describe("when the joinPointResolver is called", () => { + it("should return a path that is the same as the variantPath, but with the last 3 directories removed", () => { + const variantPath = + "/test-folder/test-sub-folder/test-sub-folder/test-sub-folder/test-variant"; + const joinPointPath = result.joinPointResolver(variantPath); + expect(joinPointPath).toEqual("/test-folder/test-variant"); + }); + }); + }; + + const variantGlobs = Symbol("test-variant-globs"); + const joinPointResolver = Symbol("test-join-point-resolver"); + + const defaultVariantGlobs = [ + "./**/__variants__/*/*/!(*.test).{js,jsx,ts,tsx}" + ]; + const defaultJoinPointResolver = expect.any(Function); + const defaultToggleHandler = + "@asos/web-toggle-point-webpack/pathSegmentToggleHandler"; + const toggleHandler = Symbol("test-toggle-handler"); + const webpackNormalModule = Symbol("test-webpack-normal-module"); + + describe("when configuring the plugin with a supplied webpackNormalModule", () => { + beforeEach(() => { + result = fillDefaultOptionalValues({ + webpackNormalModule, + pointCuts: [] + }); + }); + + it("should return the supplied webpackNormalModule", () => { + expect(result.webpackNormalModule).toBe(webpackNormalModule); + }); + }); + + describe("when configuring the plugin without supplying a webpackNormalModule", () => { + beforeEach(() => { + result = fillDefaultOptionalValues({ + pointCuts: [] + }); + }); + + it("should return the NormalModule from the webpack import", () => { + expect(result.webpackNormalModule).toBe(webpack.NormalModule); + }); + }); + + describe.each` + variantGlobs | joinPointResolver | toggleHandler | description | expectation + ${undefined} | ${undefined} | ${undefined} | ${"nothing"} | ${{ variantGlobs: defaultVariantGlobs, joinPointResolver: defaultJoinPointResolver, toggleHandler: defaultToggleHandler }} + ${variantGlobs} | ${undefined} | ${undefined} | ${"a variantGlob, but nothing else"} | ${{ variantGlobs, joinPointResolver: defaultJoinPointResolver, toggleHandler: defaultToggleHandler }} + ${variantGlobs} | ${joinPointResolver} | ${undefined} | ${"a variantGlob and a join point resolver"} | ${{ variantGlobs, joinPointResolver, toggleHandler: defaultToggleHandler }} + ${undefined} | ${joinPointResolver} | ${undefined} | ${"a joinPointResolver, but nothing else"} | ${{ variantGlobs: defaultVariantGlobs, joinPointResolver, toggleHandler: defaultToggleHandler }} + ${undefined} | ${undefined} | ${toggleHandler} | ${"a toggle handler "} | ${{ variantGlobs: defaultVariantGlobs, joinPointResolver: defaultJoinPointResolver, toggleHandler }} + ${variantGlobs} | ${undefined} | ${toggleHandler} | ${"a toggle handler and a variantGlob, but nothing else"} | ${{ variantGlobs, joinPointResolver: defaultJoinPointResolver, toggleHandler }} + ${variantGlobs} | ${joinPointResolver} | ${toggleHandler} | ${"a toggle handler, a variantGlob and a join point resolver"} | ${{ variantGlobs, joinPointResolver, toggleHandler }} + ${undefined} | ${joinPointResolver} | ${toggleHandler} | ${"a toggle handler and a joinPointResolver, but nothing else"} | ${{ variantGlobs: defaultVariantGlobs, joinPointResolver, toggleHandler }} + `( + "when configuring pointCuts, supplying $description", + // eslint-disable-next-line no-unused-vars + ({ expectation, description, ...pointCut }) => { + beforeEach(async () => { + result = fillDefaultOptionalValues({ pointCuts: [pointCut] }); + }); + + it("should fill the defaults", () => { + expect(result.pointCuts[0]).toEqual(expectation); + }); + + if (!joinPointResolver) { + makeDefaultJoinPointResolverAssertions(); + } + } + ); +}); diff --git a/packages/webpack/src/plugins/togglePointInjection/index.js b/packages/webpack/src/plugins/togglePointInjection/index.js index 12d32da..c78aa96 100644 --- a/packages/webpack/src/plugins/togglePointInjection/index.js +++ b/packages/webpack/src/plugins/togglePointInjection/index.js @@ -1,11 +1,12 @@ import processPointCuts from "./processPointCuts/index.js"; import Logger from "./logger.js"; -import { win32, posix } from "path"; +import { sep, posix } from "path"; import { PLUGIN_NAME } from "./constants.js"; import resolveJoinPoints from "./resolveJoinPoints/index.js"; import setupSchemeModules from "./setupSchemeModules/index.js"; import { validate } from "schema-utils"; import schema from "./schema.json"; +import fillDefaultOptionalValues from "./fillDefaultOptionalValues.js"; /** * Toggle Point Injection Plugin @@ -21,8 +22,8 @@ class TogglePointInjection { * @param {string} options.pointCuts[].togglePointModule path, from root of the compilation, of where the toggle point sits. Or a resolvable node_module. * @param {string[]} [options.pointCuts[].variantGlobs=[.\/**\/__variants__/*\/*\/!(*.test).{js,jsx,ts,tsx}]] {@link https://en.wikipedia.org/wiki/Glob_(programming)|Globs} to identified variant modules. The plugin uses {@link https://github.com/mrmlnc/fast-glob|fast-glob} under the hood, so supports any glob that it does. * @param {function} [options.pointCuts[].joinPointResolver=(variantPath) => path.posix.resolve(variantPath, "../../../..", path.basename(variantPath))] A function that takes the path to a variant module and returns a join point / base module. N.B. This is executed at build-time, so cannot use run-time context. It should use posix path segments, so on Windows be sure to use path.posix.resolve. - * @param {string} [options.pointCuts[].toggleHandler] Path to a toggle handler that unpicks a {@link https://webpack.js.org/api/module-methods/#requirecontext|require.context} containing potential variants, passing that plus a joint point module to a toggle point function. If not provided, the plugin will use a default handler that processes folder names into a tree held in a Map. Leaf nodes of the tree are the variant modules. - * @param {function} [options.webpackNormalModule] A function that returns the Webpack NormalModule class. This is required for Next.js, as it does not expose the NormalModule class directly + * @param {string} [options.pointCuts[].toggleHandler='@asos/web-toggle-point-webpack/pathSegmentToggleHandler'] a {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#importing_features_into_your_script|module specifier} pointing to a toggle handler, that takes a toggle point, a Map of relative paths to potential variants, and a join point. If not provided, the plugin will use a default handler that processes folder names into a tree held in a Map. + * @param {function} [options.webpackNormalModule] A reference to the Webpack NormalModule class. This is required for Next.js, as it does not expose the NormalModule class directly * @returns {external:Webpack.WebpackPluginInstance} WebpackPluginInstance * @example N.B. forward slashes are escaped in the glob, due to JSDoc shortcomings, but in reality should be un-escaped * const plugin = new TogglePointInjection({ @@ -44,23 +45,19 @@ class TogglePointInjection { */ constructor(options) { validate(schema, options, { name: PLUGIN_NAME, baseDataPath: "options" }); - this.options = { - webpackNormalModule: async () => - (await import("webpack")).default.NormalModule, - ...options - }; + this.options = fillDefaultOptionalValues(options); } apply(compiler) { let NormalModule, joinPointFiles, warnings, appRoot; compiler.hooks.beforeCompile.tapPromise(PLUGIN_NAME, async () => { - appRoot = compiler.context.replaceAll(win32.sep, posix.sep); + appRoot = compiler.context.replaceAll(sep, posix.sep); ({ joinPointFiles, warnings } = await processPointCuts({ appRoot, fileSystem: compiler.inputFileSystem, options: this.options })); - NormalModule = await this.options.webpackNormalModule(); + NormalModule = await this.options.webpackNormalModule; }); compiler.hooks.compilation.tap( diff --git a/packages/webpack/src/plugins/togglePointInjection/index.test.js b/packages/webpack/src/plugins/togglePointInjection/index.test.js index 472063b..d2db6bb 100644 --- a/packages/webpack/src/plugins/togglePointInjection/index.test.js +++ b/packages/webpack/src/plugins/togglePointInjection/index.test.js @@ -4,7 +4,7 @@ import { PLUGIN_NAME } from "./constants.js"; import resolveJoinPoints from "./resolveJoinPoints/index.js"; import setupSchemeModules from "./setupSchemeModules/index.js"; import TogglePointInjection from "./index.js"; -import webpack from "webpack"; +import fillDefaultOptionalValues from "./fillDefaultOptionalValues.js"; import { validate } from "schema-utils"; import schema from "./schema.json"; @@ -20,9 +20,15 @@ jest.mock("./constants", () => ({ jest.mock("./processPointCuts/index.js", () => jest.fn()); jest.mock("./resolveJoinPoints/index.js", () => jest.fn()); jest.mock("./setupSchemeModules/index.js", () => jest.fn()); -jest.mock("webpack", () => ({ NormalModule: Symbol("test-normal-module") })); jest.mock("schema-utils", () => ({ validate: jest.fn() })); jest.mock("./schema.json", () => Symbol("test-json")); +const mockNormalModule = Symbol("test-normal-module"); +jest.mock("./fillDefaultOptionalValues.js", () => + jest.fn((options) => ({ + ...options, + webpackNormalModule: mockNormalModule + })) +); describe("togglePointInjection", () => { let togglePointInjection, compiler, options; @@ -40,162 +46,145 @@ describe("togglePointInjection", () => { }; }); - const makeCommonAssertions = (NormalModule) => { - it("should validate the supplied options", () => { - expect(validate).toHaveBeenCalledWith( - schema, - options, - expect.objectContaining({ name: PLUGIN_NAME, baseDataPath: "options" }) - ); - }); - - it("should tap into the beforeCompile event, indicating the plugin name", () => { - expect(compiler.hooks.beforeCompile.tapPromise).toHaveBeenCalledWith( - PLUGIN_NAME, - expect.any(Function) - ); - }); - - it("should tap into the compilation event, indicating the plugin name", () => { - expect(compiler.hooks.compilation.tap).toHaveBeenCalledWith( - PLUGIN_NAME, - expect.any(Function) - ); + describe("when a webpackNormalModule option is not supplied", () => { + beforeEach(() => { + options = { pointCuts }; + togglePointInjection = new TogglePointInjection(options); }); - describe("when the beforeCompile event is triggered", () => { - let beforeCompileCallback, resolve, result; - const warnings = Symbol("test-warnings"); - const compilation = Symbol("test-compilation"); - const normalModuleFactory = Symbol("test-normal-module-factory"); - + describe("when applying to a compiler", () => { beforeEach(() => { - processPointCuts.mockReturnValue( - new Promise((res) => { - resolve = res; + togglePointInjection.apply(compiler); + }); + + it("should validate the supplied options", () => { + expect(validate).toHaveBeenCalledWith( + schema, + options, + expect.objectContaining({ + name: PLUGIN_NAME, + baseDataPath: "options" }) ); - [, beforeCompileCallback] = - compiler.hooks.beforeCompile.tapPromise.mock.lastCall; - result = beforeCompileCallback(); }); - it("should process the supplied point cuts, returning only when processed", () => { - expect(processPointCuts).toHaveBeenCalledWith({ - appRoot: compiler.context, - fileSystem: compiler.inputFileSystem, - options: togglePointInjection.options - }); + it("should fill in default optional values", () => { + expect(fillDefaultOptionalValues).toHaveBeenCalledWith(options); }); - const makeCommonAssertions = () => { - it("should create a logger for the compilation", () => { - expect(Logger).toHaveBeenCalledWith(compilation); - }); + it("should tap into the beforeCompile event, indicating the plugin name", () => { + expect(compiler.hooks.beforeCompile.tapPromise).toHaveBeenCalledWith( + PLUGIN_NAME, + expect.any(Function) + ); + }); + + it("should tap into the compilation event, indicating the plugin name", () => { + expect(compiler.hooks.compilation.tap).toHaveBeenCalledWith( + PLUGIN_NAME, + expect.any(Function) + ); + }); - it("should log any warnings", () => { - expect(Logger.mock.instances[0].logWarnings).toHaveBeenCalledWith( - warnings + describe("when the beforeCompile event is triggered", () => { + let beforeCompileCallback, resolve, result; + const warnings = Symbol("test-warnings"); + const compilation = Symbol("test-compilation"); + const normalModuleFactory = Symbol("test-normal-module-factory"); + + beforeEach(() => { + processPointCuts.mockReturnValue( + new Promise((res) => { + resolve = res; + }) ); + [, beforeCompileCallback] = + compiler.hooks.beforeCompile.tapPromise.mock.lastCall; + result = beforeCompileCallback(); }); - }; - - describe("when the point cuts are processed, and some are found", () => { - const joinPointFiles = new Set([Symbol("test-join-point-file")]); - beforeEach(async () => { - resolve({ joinPointFiles, warnings }); - await result; + it("should process the supplied point cuts, returning only when processed", () => { + expect(processPointCuts).toHaveBeenCalledWith({ + appRoot: compiler.context, + fileSystem: compiler.inputFileSystem, + options: togglePointInjection.options + }); }); - describe("when the compilation event is triggered", () => { - beforeEach(() => { - const [, compilationCallback] = - compiler.hooks.compilation.tap.mock.lastCall; - compilationCallback(compilation, { normalModuleFactory }); + const makeCommonAssertions = () => { + it("should create a logger for the compilation", () => { + expect(Logger).toHaveBeenCalledWith(compilation); }); - makeCommonAssertions(); - - it("should log the join points", () => { - expect(Logger.mock.instances[0].logJoinPoints).toHaveBeenCalledWith( - joinPointFiles + it("should log any warnings", () => { + expect(Logger.mock.instances[0].logWarnings).toHaveBeenCalledWith( + warnings ); }); + }; - it("should set up scheme modules", () => { - expect(setupSchemeModules).toHaveBeenCalledWith({ - NormalModule, - compilation, - joinPointFiles, - pointCuts - }); - }); + describe("when the point cuts are processed, and some are found", () => { + const joinPointFiles = new Set([Symbol("test-join-point-file")]); - it("should resolve the join points", () => { - expect(resolveJoinPoints).toHaveBeenCalledWith({ - compilation, - appRoot: compiler.context, - normalModuleFactory, - joinPointFiles - }); + beforeEach(async () => { + resolve({ joinPointFiles, warnings }); + await result; }); - }); - }); - - describe("when the point cuts are processed, and none are found", () => { - beforeEach(async () => { - resolve({ joinPointFiles: [], warnings }); - await result; - }); - describe("when the compilation event is triggered", () => { - beforeEach(() => { - const [, compilationCallback] = - compiler.hooks.compilation.tap.mock.lastCall; - compilationCallback(compilation, { - normalModuleFactory + describe("when the compilation event is triggered", () => { + beforeEach(() => { + const [, compilationCallback] = + compiler.hooks.compilation.tap.mock.lastCall; + compilationCallback(compilation, { normalModuleFactory }); }); - }); - makeCommonAssertions(); - }); - }); - }); - }; + makeCommonAssertions(); - describe("when a webpackNormalModule option is not supplied", () => { - beforeEach(() => { - options = { pointCuts }; - togglePointInjection = new TogglePointInjection(options); - }); + it("should log the join points", () => { + expect( + Logger.mock.instances[0].logJoinPoints + ).toHaveBeenCalledWith(joinPointFiles); + }); - describe("when applying to a compiler", () => { - beforeEach(() => { - togglePointInjection.apply(compiler); - }); + it("should set up scheme modules", () => { + expect(setupSchemeModules).toHaveBeenCalledWith({ + NormalModule: mockNormalModule, + compilation, + joinPointFiles, + pointCuts + }); + }); - makeCommonAssertions(webpack.NormalModule); - }); - }); + it("should resolve the join points", () => { + expect(resolveJoinPoints).toHaveBeenCalledWith({ + compilation, + appRoot: compiler.context, + normalModuleFactory, + joinPointFiles + }); + }); + }); + }); - describe("when a webpackNormalModule option is supplied (primarily for NextJs users to get around the fact that webpack is pre-bundled)", () => { - const MockNormalModule = Symbol("test-normal-module"); + describe("when the point cuts are processed, and none are found", () => { + beforeEach(async () => { + resolve({ joinPointFiles: [], warnings }); + await result; + }); - beforeEach(() => { - options = { - pointCuts, - webpackNormalModule: async () => MockNormalModule - }; - togglePointInjection = new TogglePointInjection(options); - }); + describe("when the compilation event is triggered", () => { + beforeEach(() => { + const [, compilationCallback] = + compiler.hooks.compilation.tap.mock.lastCall; + compilationCallback(compilation, { + normalModuleFactory + }); + }); - describe("when applying to a compiler", () => { - beforeEach(() => { - togglePointInjection.apply(compiler); + makeCommonAssertions(); + }); + }); }); - - makeCommonAssertions(MockNormalModule); }); }); }); diff --git a/packages/webpack/src/plugins/togglePointInjection/integration.test.js b/packages/webpack/src/plugins/togglePointInjection/integration.test.js index d28aa06..07ec825 100644 --- a/packages/webpack/src/plugins/togglePointInjection/integration.test.js +++ b/packages/webpack/src/plugins/togglePointInjection/integration.test.js @@ -1,6 +1,6 @@ import { build } from "webpack-test-utils"; import { readFile } from "fs/promises"; -import { resolve } from "path"; +import { posix } from "path"; import TogglePointInjection from "./index.js"; import { PLUGIN_NAME } from "./constants.js"; @@ -34,7 +34,7 @@ describe("togglePointInjection", () => { fileSystem = { "node_modules/@asos/web-toggle-point-webpack/pathSegmentToggleHandler": await readFile( - resolve( + posix.resolve( __dirname, "..", "..", @@ -102,7 +102,7 @@ describe("togglePointInjection", () => { it("should log the fact that the toggle point was found", () => { expect(getLogOfType("info")).toEqual( - `Identified '${name}' point cut for join point '${modulesFolder}${moduleName}' with potential variants:\n./${variantsFolder}/${testFeature}/${testVariant}/${moduleName}` + `Identified '${name}' point cut for join point '${modulesFolder}${moduleName}' with potential variants:\n${modulesFolder}${variantsFolder}/${testFeature}/${testVariant}/${moduleName}` ); }); diff --git a/packages/webpack/src/plugins/togglePointInjection/logger.js b/packages/webpack/src/plugins/togglePointInjection/logger.js index 07ad8da..d85b6b5 100644 --- a/packages/webpack/src/plugins/togglePointInjection/logger.js +++ b/packages/webpack/src/plugins/togglePointInjection/logger.js @@ -11,14 +11,14 @@ class Logger { for (const [ joinPoint, { - variants, + variantPathMap, pointCut: { name } } ] of joinPointFiles.entries()) { this.#logger.info( - `Identified '${name}' point cut for join point '${joinPoint}' with potential variants:\n${variants.join( - "\n" - )}` + `Identified '${name}' point cut for join point '${joinPoint}' with potential variants:\n${[ + ...variantPathMap.values() + ].join("\n")}` ); } } diff --git a/packages/webpack/src/plugins/togglePointInjection/logger.test.js b/packages/webpack/src/plugins/togglePointInjection/logger.test.js index 915f876..6306bfe 100644 --- a/packages/webpack/src/plugins/togglePointInjection/logger.test.js +++ b/packages/webpack/src/plugins/togglePointInjection/logger.test.js @@ -21,12 +21,15 @@ describe("logger", () => { describe("logJoinPoints", () => { const pointCut = { name: "test-point-cut" }; const joinPointName = "test-join-point"; - const variants = ["test-variant-1", "test-variant-2"]; + const variantPathMap = new Map([ + ["test-key-1", "test-path-1"], + ["test-key-2", "test-key-2"] + ]); const joinPointFiles = new Map([ [ joinPointName, { - variants, + variantPathMap, pointCut: { name: "test-point-cut" } } ] @@ -40,9 +43,9 @@ describe("logger", () => { expect(compilationLogger.info).toHaveBeenCalledWith( `Identified '${ pointCut.name - }' point cut for join point '${joinPointName}' with potential variants:\n${variants.join( - "\n" - )}` + }' point cut for join point '${joinPointName}' with potential variants:\n${Array.from( + variantPathMap.values() + ).join("\n")}` ); }); }); diff --git a/packages/webpack/src/plugins/togglePointInjection/processPointCuts/fillDefaultOptionalValues.js b/packages/webpack/src/plugins/togglePointInjection/processPointCuts/fillDefaultOptionalValues.js deleted file mode 100644 index fecdcd5..0000000 --- a/packages/webpack/src/plugins/togglePointInjection/processPointCuts/fillDefaultOptionalValues.js +++ /dev/null @@ -1,13 +0,0 @@ -import { posix, basename } from "path"; - -const fillPointCutDefaults = (pointCut) => { - const { - variantGlobs = ["./**/__variants__/*/*/!(*.test).{js,jsx,ts,tsx}"], - joinPointResolver = (variantPath) => - posix.resolve(variantPath, ...Array(4).fill(".."), basename(variantPath)) - } = pointCut; - - return { variantGlobs, joinPointResolver }; -}; - -export default fillPointCutDefaults; diff --git a/packages/webpack/src/plugins/togglePointInjection/processPointCuts/fillDefaultOptionalValues.test.js b/packages/webpack/src/plugins/togglePointInjection/processPointCuts/fillDefaultOptionalValues.test.js deleted file mode 100644 index a199b19..0000000 --- a/packages/webpack/src/plugins/togglePointInjection/processPointCuts/fillDefaultOptionalValues.test.js +++ /dev/null @@ -1,74 +0,0 @@ -import fillPointCutDefaults from "./fillDefaultOptionalValues"; - -describe("fillDefaultOptionalValues", () => { - let result; - - const makeDefaultJoinPointResolverAssertions = () => { - describe("when the joinPointResolver is called", () => { - it("should return a path that is the same as the variantPath, but with the last 3 directories removed", () => { - const variantPath = - "/test-folder/test-sub-folder/test-sub-folder/test-sub-folder/test-variant"; - const joinPointPath = result.joinPointResolver(variantPath); - expect(joinPointPath).toEqual("/test-folder/test-variant"); - }); - }); - }; - - describe("when the point cut has no variantGlobs or joinPointResolver", () => { - const pointCut = {}; - - beforeEach(() => { - result = fillPointCutDefaults(pointCut); - }); - - it("should fill the defaults", () => { - expect(result).toEqual({ - variantGlobs: ["./**/__variants__/*/*/!(*.test).{js,jsx,ts,tsx}"], - joinPointResolver: expect.any(Function) - }); - }); - - makeDefaultJoinPointResolverAssertions(); - }); - - describe("when the point cut has a variantGlobs but no joinPointResolver", () => { - const pointCut = { variantGlobs: Symbol("test-variant-globs") }; - - beforeEach(() => { - result = fillPointCutDefaults(pointCut); - }); - - it("should fill the defaults", () => { - expect(result).toEqual({ - variantGlobs: pointCut.variantGlobs, - joinPointResolver: expect.any(Function) - }); - }); - - makeDefaultJoinPointResolverAssertions(); - }); - - describe("when the point cut has a joinPointResolver but no variantGlobs", () => { - it("should return the supplied joinPointResolver and fill default variantGlobs", () => { - const pointCut = { - joinPointResolver: Symbol("test-join-point-resolver") - }; - const result = fillPointCutDefaults(pointCut); - expect(result).toEqual({ - variantGlobs: ["./**/__variants__/*/*/!(*.test).{js,jsx,ts,tsx}"], - joinPointResolver: pointCut.joinPointResolver - }); - }); - }); - - describe("when the point cut has variantGlobs and a joinPointResolver", () => { - it("should return the point cut supplied values", () => { - const pointCut = { - variantGlobs: Symbol("test-variant-glob"), - joinPointResolver: Symbol("test-join-point-resolver") - }; - const result = fillPointCutDefaults(pointCut); - expect(result).toEqual(pointCut); - }); - }); -}); diff --git a/packages/webpack/src/plugins/togglePointInjection/processPointCuts/index.js b/packages/webpack/src/plugins/togglePointInjection/processPointCuts/index.js index 503b057..ec0d590 100644 --- a/packages/webpack/src/plugins/togglePointInjection/processPointCuts/index.js +++ b/packages/webpack/src/plugins/togglePointInjection/processPointCuts/index.js @@ -1,6 +1,5 @@ import processVariantFiles from "./processVariantFiles/index.js"; import getVariantPaths from "./getVariantPaths.js"; -import fillDefaultOptionalValues from "./fillDefaultOptionalValues.js"; const processPointCuts = async ({ appRoot, @@ -11,8 +10,7 @@ const processPointCuts = async ({ const configFiles = new Map(); const warnings = []; for await (const pointCut of pointCuts.values()) { - const { variantGlobs, joinPointResolver } = - fillDefaultOptionalValues(pointCut); + const { variantGlobs } = pointCut; const variantPaths = await getVariantPaths({ variantGlobs, @@ -24,7 +22,6 @@ const processPointCuts = async ({ variantPaths, joinPointFiles, pointCut, - joinPointResolver, warnings, configFiles, fileSystem, diff --git a/packages/webpack/src/plugins/togglePointInjection/processPointCuts/index.test.js b/packages/webpack/src/plugins/togglePointInjection/processPointCuts/index.test.js index 62768a2..71f9ecd 100644 --- a/packages/webpack/src/plugins/togglePointInjection/processPointCuts/index.test.js +++ b/packages/webpack/src/plugins/togglePointInjection/processPointCuts/index.test.js @@ -1,18 +1,11 @@ import processVariantFiles from "./processVariantFiles/index.js"; import getVariantPaths from "./getVariantPaths.js"; import processPointCuts from "./index.js"; -import fillDefaultOptionalValues from "./fillDefaultOptionalValues.js"; jest.mock("./processVariantFiles/index", () => jest.fn()); jest.mock("./getVariantPaths", () => jest.fn(() => Symbol("test-variant-files")) ); -jest.mock("./fillDefaultOptionalValues", () => - jest.fn(() => ({ - variantGlobs: Symbol("test-variant-globs"), - joinPointResolver: Symbol("test-join-point-resolver") - })) -); describe("processPointCuts", () => { const pointCuts = new Map([ @@ -34,16 +27,8 @@ describe("processPointCuts", () => { })); }); - it("should fill in default optional values for each point cut", () => { - for (const pointCut of pointCutsValues) { - expect(fillDefaultOptionalValues).toHaveBeenCalledWith(pointCut); - } - }); - it("should get variant files for each of the point cuts", () => { - for (const index of pointCutsValues.keys()) { - const { variantGlobs } = - fillDefaultOptionalValues.mock.results[index].value; + for (const { variantGlobs } of pointCutsValues.keys()) { expect(getVariantPaths).toHaveBeenCalledWith({ variantGlobs, appRoot, @@ -55,13 +40,10 @@ describe("processPointCuts", () => { it("should process the variant files, and keep a shared record of config files found between each point cut", () => { for (const [index, pointCut] of pointCutsValues.entries()) { const variantPaths = getVariantPaths.mock.results[index].value; - const { joinPointResolver } = - fillDefaultOptionalValues.mock.results[index].value; expect(processVariantFiles).toHaveBeenCalledWith({ variantPaths, joinPointFiles, pointCut, - joinPointResolver, warnings, configFiles: expect.any(Map), fileSystem, diff --git a/packages/webpack/src/plugins/togglePointInjection/processPointCuts/processVariantFiles/index.js b/packages/webpack/src/plugins/togglePointInjection/processPointCuts/processVariantFiles/index.js index 8d8e09a..33e4b5d 100644 --- a/packages/webpack/src/plugins/togglePointInjection/processPointCuts/processVariantFiles/index.js +++ b/packages/webpack/src/plugins/togglePointInjection/processPointCuts/processVariantFiles/index.js @@ -1,17 +1,20 @@ import { posix } from "path"; -import isJoinPointInvalid from "./isJoinPointInvalid"; +import isJoinPointInvalid from "./isJoinPointInvalid.js"; +import linkJoinPoints from "./linkJoinPoints.js"; const { parse, relative } = posix; +const normalizeToRelativePath = (path, joinDirectory) => + relative(joinDirectory, path).replace(/^([^./])/, "./$1"); + const processVariantFiles = async ({ variantPaths, joinPointFiles, pointCut, - joinPointResolver, warnings, ...rest }) => { for (const variantPath of variantPaths) { - const joinPointPath = joinPointResolver(variantPath); + const joinPointPath = pointCut.joinPointResolver(variantPath); const { dir: directory, base: filename } = parse(joinPointPath); if (!joinPointFiles.has(joinPointPath)) { @@ -27,7 +30,7 @@ const processVariantFiles = async ({ } joinPointFiles.set(joinPointPath, { pointCut, - variants: [] + variantPathMap: new Map() }); } @@ -39,10 +42,11 @@ const processVariantFiles = async ({ continue; } - joinPointFile.variants.push( - relative(directory, variantPath).replace(/^([^./])/, "./$1") - ); + const key = normalizeToRelativePath(variantPath, directory); + joinPointFile.variantPathMap.set(key, variantPath); } + + linkJoinPoints(joinPointFiles); }; export default processVariantFiles; diff --git a/packages/webpack/src/plugins/togglePointInjection/processPointCuts/processVariantFiles/index.test.js b/packages/webpack/src/plugins/togglePointInjection/processPointCuts/processVariantFiles/index.test.js index 1ff96c5..4a2a4c2 100644 --- a/packages/webpack/src/plugins/togglePointInjection/processPointCuts/processVariantFiles/index.test.js +++ b/packages/webpack/src/plugins/togglePointInjection/processPointCuts/processVariantFiles/index.test.js @@ -1,41 +1,37 @@ -import processVariantFiles from "."; -import { memfs } from "memfs"; import { posix } from "path"; +import isJoinPointInvalid from "./isJoinPointInvalid.js"; +import linkJoinPoints from "./linkJoinPoints.js"; +import processVariantFiles from "./index.js"; + const { resolve, join, sep } = posix; +jest.mock("./linkJoinPoints.js", () => jest.fn()); +jest.mock("./isJoinPointInvalid.js", () => jest.fn()); + describe("processVariantFiles", () => { let joinPointFiles; - const pointCut = { name: "test-point-cut" }; - const joinPointResolver = jest.fn(); + const pointCut = { name: "test-point-cut", joinPointResolver: jest.fn() }; let warnings; const variantFileGlob = "test-variant-*.*"; - const variantGlobs = [`/${variantFileGlob}`]; - const appRoot = "/test-app-root/"; const moduleFile = "test-module.js"; const joinPointFolder = "test-folder"; const joinPointPath = join(joinPointFolder, moduleFile); - const { fs: fileSystem } = memfs({ - [`${appRoot}${joinPointPath}`]: "join point" - }); + const rest = { [Symbol("test-key")]: Symbol("test-value") }; - beforeEach(() => { + beforeEach(async () => { + jest.clearAllMocks(); warnings = []; joinPointFiles = new Map(); }); - const act = async ({ variantPaths, configFiles }) => { + const act = async ({ variantPaths }) => { await processVariantFiles({ variantPaths, - configFiles, joinPointFiles, pointCut, - joinPointResolver, - variantGlobs, warnings, - name: moduleFile, - fileSystem, - appRoot + ...rest }); }; @@ -61,14 +57,41 @@ describe("processVariantFiles", () => { `( "when given a variant path ($variantPath)", ({ variantPath, expectedVariant }) => { + const path = resolve(joinPointFolder, variantPath); const variantPaths = new Set([resolve(joinPointFolder, variantPath)]); - describe("when given a variant file that has no matching join point file", () => { + const makeCommonAssertions = () => { + it("should call the joinPointResolver with the path to the variant file", () => { + expect(pointCut.joinPointResolver).toHaveBeenCalledWith(path); + }); + + it("should call linkJoinPoints with the updated joinPointFiles", () => { + expect(linkJoinPoints).toHaveBeenCalledWith(joinPointFiles); + }); + }; + + describe("when given a variant file that is not valid according to the configured config files", () => { + const filename = "test-not-matching-control"; + const joinPointPath = join(joinPointFolder, filename); + beforeEach(async () => { - joinPointResolver.mockReturnValue( - join(joinPointFolder, "test-not-matching-control") - ); - await act({ variantPaths, configFiles: new Map() }); + pointCut.joinPointResolver.mockReturnValue(joinPointPath); + isJoinPointInvalid.mockReturnValue(true); + await act({ + variantPaths, + configFiles: new Map() + }); + }); + + makeCommonAssertions(); + + it("should call isJoinPointInvalid with the expected arguments", () => { + expect(isJoinPointInvalid).toHaveBeenCalledWith({ + filename, + joinPointPath, + directory: joinPointFolder, + ...rest + }); }); it("should add no warnings, and not modify joinPointFiles", async () => { @@ -79,41 +102,27 @@ describe("processVariantFiles", () => { describe("when given a variant file that has a matching join point file", () => { beforeEach(async () => { - joinPointResolver.mockReturnValue(joinPointPath); + pointCut.joinPointResolver.mockReturnValue(joinPointPath); }); describe("and no config file precludes it being valid", () => { beforeEach(async () => { - await act({ variantPaths, configFiles: new Map() }); + isJoinPointInvalid.mockReturnValue(false); + await act({ variantPaths }); }); - it("should add no warnings, and add a single joinPointFile representing the matched join point", async () => { - expect(warnings).toEqual([]); - expect(joinPointFiles).toEqual( - new Map([ - [ - joinPointPath, - { - pointCut, - variants: [expectedVariant] - } - ] - ]) - ); - }); - }); + makeCommonAssertions(); - describe("and a config file confirms it as valid", () => { - beforeEach(async () => { - await act({ - variantPaths, - configFiles: new Map([ - [joinPointFolder, { joinPoints: [moduleFile] }] - ]) + it("should call isJoinPointInvalid with the expected arguments", () => { + expect(isJoinPointInvalid).toHaveBeenCalledWith({ + filename: moduleFile, + joinPointPath, + directory: joinPointFolder, + ...rest }); }); - it("should add no warnings, and add a single joinPointFile representing the matched join point", async () => { + it("should add no warnings, and add a single joinPointFile representing the matched join point, relative to the control module", async () => { expect(warnings).toEqual([]); expect(joinPointFiles).toEqual( new Map([ @@ -121,7 +130,7 @@ describe("processVariantFiles", () => { joinPointPath, { pointCut, - variants: [expectedVariant] + variantPathMap: new Map([[expectedVariant, path]]) } ] ]) @@ -129,20 +138,6 @@ describe("processVariantFiles", () => { }); }); - describe("and a config file precludes it from being valid", () => { - beforeEach(async () => { - await act({ - variantPaths, - configFiles: new Map([[joinPointFolder, { joinPoints: [] }]]) - }); - }); - - it("should add no warnings, and not modify joinPointFiles", async () => { - expect(warnings).toEqual([]); - expect(joinPointFiles).toEqual(new Map()); - }); - }); - describe("and a preceding point cut already identified the join point", () => { const testOtherPointCut = { name: "test-other-point-cut" }; beforeEach(async () => { @@ -150,11 +145,16 @@ describe("processVariantFiles", () => { pointCut: testOtherPointCut }); await act({ - variantPaths, - configFiles: new Map() + variantPaths }); }); + makeCommonAssertions(); + + it("should not check if the join point is invalid again", () => { + expect(isJoinPointInvalid).not.toHaveBeenCalled(); + }); + it("should add a warning, and not modify joinPointFiles", async () => { expect(warnings).toEqual([ `Join point "${joinPointPath}" is already assigned to point cut "${testOtherPointCut.name}". Skipping assignment to "${pointCut.name}".` diff --git a/packages/webpack/src/plugins/togglePointInjection/processPointCuts/processVariantFiles/linkJoinPoints.js b/packages/webpack/src/plugins/togglePointInjection/processPointCuts/processVariantFiles/linkJoinPoints.js new file mode 100644 index 0000000..4b6dd65 --- /dev/null +++ b/packages/webpack/src/plugins/togglePointInjection/processPointCuts/processVariantFiles/linkJoinPoints.js @@ -0,0 +1,13 @@ +import { JOIN_POINTS, SCHEME } from "../../constants.js"; + +const linkJoinPoints = (joinPointFiles) => { + for (const [, { variantPathMap }] of joinPointFiles) { + for (const [key, path] of variantPathMap) { + if (joinPointFiles.has(path)) { + variantPathMap.set(key, `${SCHEME}:${JOIN_POINTS}:${path}`); + } + } + } +}; + +export default linkJoinPoints; diff --git a/packages/webpack/src/plugins/togglePointInjection/processPointCuts/processVariantFiles/linkJoinPoints.test.js b/packages/webpack/src/plugins/togglePointInjection/processPointCuts/processVariantFiles/linkJoinPoints.test.js new file mode 100644 index 0000000..ad8b4ff --- /dev/null +++ b/packages/webpack/src/plugins/togglePointInjection/processPointCuts/processVariantFiles/linkJoinPoints.test.js @@ -0,0 +1,109 @@ +import linkJoinPoints from "./linkJoinPoints"; +import { JOIN_POINTS, SCHEME } from "../../constants.js"; + +jest.mock("../../constants.js", () => ({ + JOIN_POINTS: "mockedJoinPoints", + SCHEME: "mockedScheme" +})); + +describe("linkJoinPoints", () => { + let mockJoinPoint1, mockJoinPoint2; + beforeEach(() => { + mockJoinPoint1 = [ + "path/to/joinPoint1", + { + variantPathMap: new Map([ + ["path/to/jointPoint1/variant1", "path/to/jointPoint1/variant1"], + ["path/to/jointPoint1/variant2", "path/to/jointPoint1/variant2"] + ]) + } + ]; + mockJoinPoint2 = [ + "path/to/joinPoint2", + { + variantPathMap: new Map([ + ["path/to/jointPoint2/variant1", "path/to/jointPoint2/variant1"], + ["path/to/jointPoint2/variant2", "path/to/jointPoint2/variant2"] + ]) + } + ]; + }); + + describe("when join points are not chained", () => { + it("should not modify the join point files", () => { + const joinPointFiles = new Map([mockJoinPoint1, mockJoinPoint2]); + linkJoinPoints(joinPointFiles); + + expect(joinPointFiles).toEqual( + new Map([ + [ + "path/to/joinPoint1", + { + variantPathMap: new Map([ + [ + "path/to/jointPoint1/variant1", + "path/to/jointPoint1/variant1" + ], + ["path/to/jointPoint1/variant2", "path/to/jointPoint1/variant2"] + ]) + } + ], + [ + "path/to/joinPoint2", + { + variantPathMap: new Map([ + [ + "path/to/jointPoint2/variant1", + "path/to/jointPoint2/variant1" + ], + ["path/to/jointPoint2/variant2", "path/to/jointPoint2/variant2"] + ]) + } + ] + ]) + ); + }); + }); + + describe("when a variant of a join point is itself a join point", () => { + it("should link variants of the join point to the connected join point", () => { + const connectedJoinPoint2 = [ + "path/to/jointPoint1/variant1", + mockJoinPoint2[1] + ]; + + const joinPointFiles = new Map([mockJoinPoint1, connectedJoinPoint2]); + + linkJoinPoints(joinPointFiles); + + expect(joinPointFiles).toEqual( + new Map([ + [ + "path/to/joinPoint1", + { + variantPathMap: new Map([ + [ + "path/to/jointPoint1/variant1", + `${SCHEME}:${JOIN_POINTS}:path/to/jointPoint1/variant1` + ], + ["path/to/jointPoint1/variant2", "path/to/jointPoint1/variant2"] + ]) + } + ], + [ + "path/to/jointPoint1/variant1", + { + variantPathMap: new Map([ + [ + "path/to/jointPoint2/variant1", + "path/to/jointPoint2/variant1" + ], + ["path/to/jointPoint2/variant2", "path/to/jointPoint2/variant2"] + ]) + } + ] + ]) + ); + }); + }); +}); diff --git a/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/index.js b/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/index.js index 9f36121..40e24f2 100644 --- a/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/index.js +++ b/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/index.js @@ -1,5 +1,5 @@ import { PLUGIN_NAME } from "../constants"; -import { posix, win32 } from "path"; +import { sep, posix } from "path"; import { promisify } from "util"; import handleJoinPointMatch from "./handleJoinPointMatch"; const { relative } = posix; @@ -7,6 +7,33 @@ const { relative } = posix; const isLoaderlessFileRequest = (request) => [".", "/"].includes(request.at(0)) && !request.includes("!"); +const matchJoinPointIfResolved = async ({ + enhancedResolve, + resolveData, + appRoot, + joinPointFiles, + compilation +}) => { + const resolved = await enhancedResolve( + {}, + resolveData.context, + resolveData.request, + {} + ); + if (!resolved) { + return; + } + + const resource = `/${relative(appRoot, resolved.replaceAll(sep, posix.sep))}`; + if (joinPointFiles.has(resource)) { + handleJoinPointMatch({ + resource, + compilation, + resolveData + }); + } +}; + const resolveJoinPoints = ({ compilation, appRoot, @@ -23,29 +50,19 @@ const resolveJoinPoints = ({ async (resolveData) => { if ( !joinPointFiles.size || - !resolveData.context - .replaceAll(win32.sep, posix.sep) - .startsWith(appRoot) || + !resolveData.context.replaceAll(sep, posix.sep).startsWith(appRoot) || !isLoaderlessFileRequest(resolveData.request) ) { return; } - const resolved = await enhancedResolve( - {}, - resolveData.context, - resolveData.request, - {} - ); - - const resource = `/${relative(appRoot, resolved.replaceAll(win32.sep, posix.sep))}`; - if (joinPointFiles.has(resource)) { - handleJoinPointMatch({ - resource, - compilation, - resolveData - }); - } + await matchJoinPointIfResolved({ + appRoot, + joinPointFiles, + enhancedResolve, + resolveData, + compilation + }); } ); }; diff --git a/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/index.test.js b/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/index.test.js index e9628c8..b08d897 100644 --- a/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/index.test.js +++ b/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/index.test.js @@ -1,7 +1,7 @@ import { PLUGIN_NAME } from "../constants"; import handleJoinPointMatch from "./handleJoinPointMatch"; import { sep, join } from "path"; -import resolvePointCuts from "."; +import resolveJoinPoints from "."; jest.mock("../constants", () => ({ PLUGIN_NAME: "test-plugin-name" @@ -48,7 +48,7 @@ describe("resolveJoinPoints", () => { const joinPointFiles = new Map(); beforeEach(() => { - resolvePointCuts({ + resolveJoinPoints({ compilation, appRoot, normalModuleFactory, @@ -64,7 +64,7 @@ describe("resolveJoinPoints", () => { beforeEach(() => { [, beforeResolveCallback] = normalModuleFactory.hooks.beforeResolve.tapPromise.mock.lastCall; - handleJoinPointMatch.mockClear(); + jest.clearAllMocks(); beforeResolveCallback(); }); @@ -77,7 +77,7 @@ describe("resolveJoinPoints", () => { describe("when there are some join points previously identified", () => { const joinPointFile = "/test-folder/test-join-point-file"; beforeEach(() => { - resolvePointCuts({ + resolveJoinPoints({ compilation, appRoot, normalModuleFactory, @@ -157,6 +157,19 @@ describe("resolveJoinPoints", () => { }); }; + describe("and the file cannot be resolved", () => { + beforeEach(() => { + mockResolvedResource = false; + beforeResolveCallback(resolveData); + }); + + makeCommonAssertions(); + + it("should not try to handle a match", () => { + expect(handleJoinPointMatch).not.toHaveBeenCalled(); + }); + }); + describe("and the file is not a join point", () => { beforeEach(() => { mockResolvedResource = join(appRoot, "some-other-file"); diff --git a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint.js b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint.js deleted file mode 100644 index cab2b71..0000000 --- a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint.js +++ /dev/null @@ -1,20 +0,0 @@ -import { posix } from "path"; -import regexgen from "regexgen"; -import { POINT_CUTS, SCHEME } from "../constants.js"; -const { dirname } = posix; - -const generateJoinPoint = ({ joinPointFiles, path }) => { - const { - pointCut: { name }, - variants - } = joinPointFiles.get(path); - const directory = dirname(path); - const regex = regexgen(variants); - - return `import pointCut from "${SCHEME}:${POINT_CUTS}:/${name}"; -import * as joinPoint from "${path}"; -const variants = import.meta.webpackContext("${directory}", { recursive: true, regExp: ${regex} }); -export default pointCut({ joinPoint, variants });`; -}; - -export default generateJoinPoint; diff --git a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint.test.js b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint.test.js deleted file mode 100644 index d03a273..0000000 --- a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint.test.js +++ /dev/null @@ -1,47 +0,0 @@ -import { POINT_CUTS, SCHEME } from "../constants.js"; -import generateJoinPoint from "./generateJoinPoint.js"; - -jest.mock("../constants", () => ({ - SCHEME: "test-scheme", - POINT_CUTS: "test-point-cuts" -})); - -describe("generateJoinPoint", () => { - const path = "test-folder/test-path"; - const pointCutName = "test-point-cut"; - const variants = [ - "test-sub-folder/test-variant-1", - "test-sub-folder/test-variant-2", - "test-other-sub-folder/test-variant-1" - ]; - let result; - - beforeEach(() => { - const joinPointFiles = new Map([ - [path, { pointCut: { name: pointCutName }, variants }] - ]); - result = generateJoinPoint({ joinPointFiles, path }); - }); - - it("should return a script that imports the appropriate point cut for the join point", () => { - expect(result).toMatch( - `import pointCut from "${SCHEME}:${POINT_CUTS}:/${pointCutName}";` - ); - }); - - it("should return a script that imports the base / control module for the join point", () => { - expect(result).toMatch(`import * as joinPoint from "${path}";`); - }); - - it("should return a script that imports all the valid variants of the base / control module into a webpackContext, using a minimal regex that matches all the variants", () => { - expect(result).toMatch( - `const variants = import.meta.webpackContext("${ - path.split("/")[0] - }", { recursive: true, regExp: /test\\-(?:other\\-sub\\-folder\\/test\\-variant\\-1|sub\\-folder\\/test\\-variant\\-[12])/ });` - ); - }); - - it("should return a script exports a default export which calls the point cut, passing the join point (control module) and the variants", () => { - expect(result).toMatch("export default pointCut({ joinPoint, variants });"); - }); -}); diff --git a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/createVariantPathMap.js b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/createVariantPathMap.js new file mode 100644 index 0000000..770367f --- /dev/null +++ b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/createVariantPathMap.js @@ -0,0 +1,5 @@ +const createVariantPathMap = (content) => `const variantPathMap = new Map([ +${content} +]);`; + +export default createVariantPathMap; diff --git a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/createVariantPathMap.test.js b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/createVariantPathMap.test.js new file mode 100644 index 0000000..cabf55a --- /dev/null +++ b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/createVariantPathMap.test.js @@ -0,0 +1,11 @@ +import createVariantPathMap from "./createVariantPathMap"; + +describe("createVariantPathMap", () => { + it("should return code to create a variantPathMap constant, wrapping the supplied content in a Map", () => { + const testContent = "test-content"; + expect(createVariantPathMap(testContent)) + .toEqual(`const variantPathMap = new Map([ +${testContent} +]);`); + }); +}); diff --git a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/importCodeGenerator.js b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/importCodeGenerator.js new file mode 100644 index 0000000..49a6583 --- /dev/null +++ b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/importCodeGenerator.js @@ -0,0 +1,15 @@ +import createVariantPathMap from "./createVariantPathMap.js"; + +const importCodeGenerator = ({ joinPointPath, variantPathMap }) => { + const variantsKeys = Array.from(variantPathMap.keys()); + return `import * as joinPoint from "${joinPointPath}"; +${variantsKeys + .map( + (key, index) => + `import * as variant_${index} from "${variantPathMap.get(key)}";` + ) + .join("\n")} +${createVariantPathMap(variantsKeys.map((key, index) => ` ["${key}", variant_${index}]`).join(",\n"))}`; +}; + +export default importCodeGenerator; diff --git a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/importCodeGenerator.test.js b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/importCodeGenerator.test.js new file mode 100644 index 0000000..f80d411 --- /dev/null +++ b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/importCodeGenerator.test.js @@ -0,0 +1,44 @@ +import importCodeGenerator from "./importCodeGenerator"; + +describe("importCodeGenerator", () => { + const joinPointPath = "/test-folder/test-path"; + const relativePaths = [ + "/test-sub-folder/test-variant-1", + "/test-sub-folder/test-variant-2", + "/test-other-sub-folder/test-variant-1" + ]; + const variantPathMap = new Map( + relativePaths.map((relativePath) => [ + relativePath, + `${joinPointPath}${relativePath}` + ]) + ); + + let result; + + beforeEach(() => { + result = importCodeGenerator({ + joinPointPath, + variantPathMap + }); + }); + + it("should return code that imports the base / control module for the join point", () => { + expect(result).toMatch(`import * as joinPoint from "${joinPointPath}";`); + }); + + it("should return code that imports all the valid variants of the base / control module, storing in variables", () => { + expect(result).toMatch(` +import * as variant_0 from "${joinPointPath}${relativePaths[0]}"; +import * as variant_1 from "${joinPointPath}${relativePaths[1]}"; +import * as variant_2 from "${joinPointPath}${relativePaths[2]}";`); + }); + + it("should return code that creates a Map of variants, keyed by relative path, valued as the variant module", () => { + expect(result).toMatch(`const variantPathMap = new Map([ + ["/test-sub-folder/test-variant-1", variant_0], + ["/test-sub-folder/test-variant-2", variant_1], + ["/test-other-sub-folder/test-variant-1", variant_2] +]);`); + }); +}); diff --git a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/index.js b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/index.js new file mode 100644 index 0000000..58b7ea6 --- /dev/null +++ b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/index.js @@ -0,0 +1,18 @@ +import { POINT_CUTS, SCHEME } from "../../constants.js"; +import importCodeGenerator from "./importCodeGenerator.js"; + +const generateJoinPoint = ({ joinPointFiles, joinPointPath }) => { + const { + pointCut: { name }, + variantPathMap + } = joinPointFiles.get(joinPointPath); + const pointCutImport = `import pointCut from "${SCHEME}:${POINT_CUTS}:/${name}";`; + + const code = importCodeGenerator({ joinPointPath, variantPathMap }); + + return `${pointCutImport} +${code} +export default pointCut({ joinPoint, variantPathMap });`; +}; + +export default generateJoinPoint; diff --git a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/index.test.js b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/index.test.js new file mode 100644 index 0000000..3a114d8 --- /dev/null +++ b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generateJoinPoint/index.test.js @@ -0,0 +1,51 @@ +import { POINT_CUTS, SCHEME } from "../../constants.js"; +import generateJoinPoint from "./index.js"; +import importCodeGenerator from "./importCodeGenerator.js"; + +jest.mock("../../constants", () => ({ + SCHEME: "test-scheme", + POINT_CUTS: "test-point-cuts" +})); +const mockImportCode = + "const joinPoint = 'test-join-point'; const variantPathMap = 'test-variants';"; +jest.mock("./importCodeGenerator.js", () => jest.fn(() => mockImportCode)); + +describe("generateJoinPoint", () => { + const joinPointPath = "/test-path"; + const pointCutName = "test-point-cut"; + const variantPathMap = Symbol("test-variant-path-map"); + const pointCut = { + name: pointCutName + }; + let result; + + beforeEach(() => { + const joinPointFiles = new Map([ + [joinPointPath, { pointCut, variantPathMap }] + ]); + result = generateJoinPoint({ + joinPointFiles, + joinPointPath + }); + }); + + it("should return a script that imports the appropriate point cut for the join point", () => { + expect(result).toMatch( + `import pointCut from "${SCHEME}:${POINT_CUTS}:/${pointCutName}";` + ); + }); + + it("should call the import code generator of the passed loading strategy, and return a script that includes the import code", () => { + expect(importCodeGenerator).toHaveBeenCalledWith({ + joinPointPath, + variantPathMap + }); + expect(result).toMatch(mockImportCode); + }); + + it("should return a script that exports a default export which calls the point cut, passing the join point (control module) and the variantPathMap returned by the import code", () => { + expect(result).toMatch( + "export default pointCut({ joinPoint, variantPathMap });" + ); + }); +}); diff --git a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generatePointCut.js b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generatePointCut.js index bb104dc..4361c83 100644 --- a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generatePointCut.js +++ b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generatePointCut.js @@ -1,5 +1,5 @@ -const generatePointCut = ({ pointCuts, path }) => { - const pointCutName = path.slice(1); +const generatePointCut = ({ pointCuts, joinPointPath }) => { + const pointCutName = joinPointPath.slice(1); const { togglePointModule, toggleHandler = "@asos/web-toggle-point-webpack/pathSegmentToggleHandler" diff --git a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generatePointCut.test.js b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generatePointCut.test.js index cbcc7d6..f825a7c 100644 --- a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generatePointCut.test.js +++ b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/generatePointCut.test.js @@ -2,7 +2,7 @@ import generatePointCut from "./generatePointCut.js"; describe("generatePointCut", () => { const pointCutName = "test-point-cut"; - const path = `/${pointCutName}`; + const joinPointPath = `/${pointCutName}`; const togglePointModule = "test-toggle-point-path"; let result, pointCuts; @@ -30,7 +30,7 @@ describe("generatePointCut", () => { beforeEach(() => { pointCuts[1].toggleHandler = toggleHandler; - result = generatePointCut({ pointCuts, path }); + result = generatePointCut({ pointCuts, joinPointPath }); }); it("should return a script that imports the appropriate toggle handler", () => { @@ -42,7 +42,7 @@ describe("generatePointCut", () => { describe("when a toggle handler is not configured against the point cut", () => { beforeEach(() => { - result = generatePointCut({ pointCuts, path }); + result = generatePointCut({ pointCuts, joinPointPath }); }); it("should return a script that imports the default toggle handler (a path segment toggle handler)", () => { diff --git a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/index.js b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/index.js index b5d014f..db42a9c 100644 --- a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/index.js +++ b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/index.js @@ -1,5 +1,5 @@ import { PLUGIN_NAME, POINT_CUTS, JOIN_POINTS, SCHEME } from "../constants.js"; -import generateJoinPoint from "./generateJoinPoint.js"; +import generateJoinPoint from "./generateJoinPoint/index.js"; import generatePointCut from "./generatePointCut.js"; const setupSchemeModules = ({ @@ -11,13 +11,13 @@ const setupSchemeModules = ({ NormalModule.getCompilationHooks(compilation) .readResource.for(SCHEME) .tap(PLUGIN_NAME, ({ resourcePath }) => { - const [, type, path] = resourcePath.split(/:(.*?):(.*)/, 3); + const [, type, joinPointPath] = resourcePath.split(/:(.*?):(.*)/, 3); switch (type) { case POINT_CUTS: { - return generatePointCut({ pointCuts, path }); + return generatePointCut({ pointCuts, joinPointPath }); } case JOIN_POINTS: { - return generateJoinPoint({ joinPointFiles, path }); + return generateJoinPoint({ joinPointFiles, joinPointPath }); } } }); diff --git a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/index.test.js b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/index.test.js index ca8a234..da55147 100644 --- a/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/index.test.js +++ b/packages/webpack/src/plugins/togglePointInjection/setupSchemeModules/index.test.js @@ -1,5 +1,5 @@ import { PLUGIN_NAME, POINT_CUTS, JOIN_POINTS, SCHEME } from "../constants.js"; -import generateJoinPoint from "./generateJoinPoint.js"; +import generateJoinPoint from "./generateJoinPoint/index.js"; import generatePointCut from "./generatePointCut.js"; import setupSchemeModules from "./index.js"; @@ -46,35 +46,42 @@ describe("setupSchemeModules", () => { let result; describe("and the resource is prefixed with the point cuts type", () => { - const path = "test-point-cut-name"; + const joinPointPath = "test-point-cut-name"; const output = Symbol("test-output"); beforeEach(() => { const [, callback] = tap.mock.lastCall; generatePointCut.mockReturnValue(output); - result = callback({ resourcePath: `${SCHEME}:${POINT_CUTS}:${path}` }); + result = callback({ + resourcePath: `${SCHEME}:${POINT_CUTS}:${joinPointPath}` + }); }); it("should generate the point cut and return the generated module to the read resource hook", () => { - expect(generatePointCut).toHaveBeenCalledWith({ pointCuts, path }); + expect(generatePointCut).toHaveBeenCalledWith({ + pointCuts, + joinPointPath + }); expect(result).toBe(output); }); }); describe("and the resource is prefixed with the join points type", () => { - const path = "test-path"; + const joinPointPath = "test-path"; const output = Symbol("test-output"); beforeEach(() => { const [, callback] = tap.mock.lastCall; generateJoinPoint.mockReturnValue(output); - result = callback({ resourcePath: `${SCHEME}:${JOIN_POINTS}:${path}` }); + result = callback({ + resourcePath: `${SCHEME}:${JOIN_POINTS}:${joinPointPath}` + }); }); it("should generate a join point and return the generated module to the read resource hook", () => { expect(generateJoinPoint).toHaveBeenCalledWith({ joinPointFiles, - path + joinPointPath }); expect(result).toBe(output); }); diff --git a/packages/webpack/src/toggleHandlers/pathSegmentToggleHandler.js b/packages/webpack/src/toggleHandlers/pathSegmentToggleHandler.js index 99a2633..5c568d3 100644 --- a/packages/webpack/src/toggleHandlers/pathSegmentToggleHandler.js +++ b/packages/webpack/src/toggleHandlers/pathSegmentToggleHandler.js @@ -1,9 +1,9 @@ -const buildTree = (map = new Map(), parts, variants, key) => { +const buildTree = (map = new Map(), parts, value) => { const [part, ...rest] = parts; if (rest.length) { - map.set(part, buildTree(map.get(part), rest, variants, key)); + map.set(part, buildTree(map.get(part), rest, value)); } else { - map.set(part, variants(key)); + map.set(part, value); } return map; }; @@ -15,14 +15,18 @@ const buildTree = (map = new Map(), parts, variants, key) => { * @param {object} options plugin options * @param {function} options.togglePoint a method that chooses the appropriate module at runtime * @param {module} options.joinPoint the join point module - * @param {string[]} options.variants an array of paths, as generated by webpack, that point to variants of the join point module + * @param {Map} params.variantPathMap a Map of posix file paths, relative to the join point module, to variant modules * @returns {function} A handler of join points injected by the plugin */ -const pathSegmentToggleHandler = ({ togglePoint, joinPoint, variants }) => { +const pathSegmentToggleHandler = ({ + togglePoint, + joinPoint, + variantPathMap +}) => { let featuresMap; - for (const key of variants.keys()) { + for (const [key, value] of variantPathMap) { const parts = key.split("/").slice(0, -1).slice(2); - featuresMap = buildTree(featuresMap, parts, variants, key); + featuresMap = buildTree(featuresMap, parts, value); } return togglePoint(joinPoint, featuresMap); }; diff --git a/packages/webpack/src/toggleHandlers/pathSegmentToggleHandler.test.js b/packages/webpack/src/toggleHandlers/pathSegmentToggleHandler.test.js index 6e64730..722624e 100644 --- a/packages/webpack/src/toggleHandlers/pathSegmentToggleHandler.test.js +++ b/packages/webpack/src/toggleHandlers/pathSegmentToggleHandler.test.js @@ -5,9 +5,7 @@ const togglePoint = jest.fn(() => toggleOutcome); const joinPoint = Symbol("mock-join-point"); describe("pathSegmentToggleHandler", () => { - let result, variantsMap; - const variants = (key) => variantsMap[key]; - variants.keys = () => Object.keys(variantsMap); + let result; beforeEach(() => { jest.clearAllMocks(); @@ -17,15 +15,25 @@ describe("pathSegmentToggleHandler", () => { const keyArray = [...Array(segmentCount).keys()]; describe(`given a list of variant paths with ${segmentCount} path segments (after the variants path)`, () => { + let variantPathMap; + beforeEach(() => { const segments = keyArray.map((key) => `test-segment-${key}/`); - variantsMap = { - [`./__variants__/${segments.join("")}test-variant.js`]: - Symbol("test-variant"), - [`./__variants__/${segments.reverse().join("")}test-variant.js`]: + variantPathMap = new Map([ + [ + `./__variants__/${segments.join("")}test-variant.js`, + Symbol("test-variant") + ], + [ + `./__variants__/${segments.reverse().join("")}test-variant.js`, Symbol("test-variant") - }; - result = pathSegmentToggleHandler({ togglePoint, joinPoint, variants }); + ] + ]); + result = pathSegmentToggleHandler({ + togglePoint, + joinPoint, + variantPathMap + }); }); it("should call the toggle point with the join point module and a map", () => { @@ -44,14 +52,14 @@ describe("pathSegmentToggleHandler", () => { }); it("should return a map containing maps for each segment, concluding with the variant at the leaf node", () => { - for (const key of Object.keys(variantsMap)) { + for (const key of Object.keys(variantPathMap)) { const segments = key.split("/").slice(0, -1); let node = map; for (const segment of segments.slice(2)) { expect(node.has(segment)).toBe(true); node = node.get(segment); } - expect(node).toBe(variantsMap[key]); + expect(node).toBe(variantPathMap[key]); } }); }); diff --git a/packages/webpack/test/test-utils.js b/packages/webpack/test/test-utils.js index 7065956..726c018 100644 --- a/packages/webpack/test/test-utils.js +++ b/packages/webpack/test/test-utils.js @@ -20,7 +20,6 @@ export const createMockGraph = ({ depth, siblingsAtEachDepthCount }) => { createGraph(rootNode, depth, siblingsAtEachDepthCount); const getIncomingConnections = jest.fn(function* (module) { - // eslint-disable-next-line jsdoc/require-jsdoc function* traverse(node) { if (node.resource === module.resource) { yield* issuersMap.get(node).map((node) => ({ From e41ef947bb543ff7cc0e29ea5dbd7512b4ac9b8b Mon Sep 17 00:00:00 2001 From: TomStrepsil <10725179+TomStrepsil@users.noreply.github.com> Date: Tue, 29 Jul 2025 21:25:35 +0100 Subject: [PATCH 06/19] use get() --- .../webpack/src/toggleHandlers/pathSegmentToggleHandler.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/webpack/src/toggleHandlers/pathSegmentToggleHandler.test.js b/packages/webpack/src/toggleHandlers/pathSegmentToggleHandler.test.js index 722624e..ecf556f 100644 --- a/packages/webpack/src/toggleHandlers/pathSegmentToggleHandler.test.js +++ b/packages/webpack/src/toggleHandlers/pathSegmentToggleHandler.test.js @@ -59,7 +59,7 @@ describe("pathSegmentToggleHandler", () => { expect(node.has(segment)).toBe(true); node = node.get(segment); } - expect(node).toBe(variantPathMap[key]); + expect(node).toBe(variantPathMap.get(key)); } }); }); From a30583bdade0eadf1df272b46a48a9f2b38e047d Mon Sep 17 00:00:00 2001 From: TomStrepsil <10725179+TomStrepsil@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:06:43 +0100 Subject: [PATCH 07/19] fix version link in CHANGELOGs to use correct date --- examples/next/docs/CHANGELOG.md | 2 +- examples/serve/docs/CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/next/docs/CHANGELOG.md b/examples/next/docs/CHANGELOG.md index e918e3c..dfd5a58 100644 --- a/examples/next/docs/CHANGELOG.md +++ b/examples/next/docs/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- update to take supply static `webpackNormalModule` corresponding to webpack plugin [version 0.9.0](../../../packages/webpack/docs/CHANGELOG.md#090---2025-05-29) +- update to take supply static `webpackNormalModule` corresponding to webpack plugin [version 0.9.0](../../../packages/webpack/docs/CHANGELOG.md#090---2025-07-29) ## [0.2.4] - 2025-05-27 diff --git a/examples/serve/docs/CHANGELOG.md b/examples/serve/docs/CHANGELOG.md index e2ca06f..17cf9f2 100644 --- a/examples/serve/docs/CHANGELOG.md +++ b/examples/serve/docs/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- updated toggle handlers to take a `variantPathMap` corresponding to webpack [version 0.9.0](../../../packages/webpack/docs/CHANGELOG.md#090---2025-05-29) +- updated toggle handlers to take a `variantPathMap` corresponding to webpack [version 0.9.0](../../../packages/webpack/docs/CHANGELOG.md#090---2025-07-29) ## [0.2.6] - 2025-07-14 From 42a078456c7d6cef435761db3a6a85100415e753 Mon Sep 17 00:00:00 2001 From: TomStrepsil <10725179+TomStrepsil@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:10:10 +0100 Subject: [PATCH 08/19] more CHANGELOG date fix --- examples/next/docs/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/next/docs/CHANGELOG.md b/examples/next/docs/CHANGELOG.md index dfd5a58..08af4aa 100644 --- a/examples/next/docs/CHANGELOG.md +++ b/examples/next/docs/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.2.5] - 2025-05-29 +## [0.2.5] - 2025-07-29 ### Changed From 904e2e53ee862f916554fbca5976c9d3395d18cc Mon Sep 17 00:00:00 2001 From: TomStrepsil <10725179+TomStrepsil@users.noreply.github.com> Date: Tue, 19 Aug 2025 10:15:47 +0100 Subject: [PATCH 09/19] handle circular references --- packages/webpack/docs/CHANGELOG.md | 1 + .../resourceProxyExistsInRequestChain.js | 6 ++ .../resourceProxyExistsInRequestChain.test.js | 63 +++++++++++++------ 3 files changed, 52 insertions(+), 18 deletions(-) diff --git a/packages/webpack/docs/CHANGELOG.md b/packages/webpack/docs/CHANGELOG.md index 66419da..18853e4 100644 --- a/packages/webpack/docs/CHANGELOG.md +++ b/packages/webpack/docs/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - removed "next" peer dependency, this needn't be explicit - ensured files that cannot be resolved (by [enhanced-resolve](https://github.com/webpack/enhanced-resolve/)), for whatever reason, don't break the build +- ensured that circular dependencies don't cause the module graph search lock up ## [0.8.0] - 2025-05-27 diff --git a/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/handleJoinPointMatch/resourceProxyExistsInRequestChain.js b/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/handleJoinPointMatch/resourceProxyExistsInRequestChain.js index 83e8d3b..bfc049c 100644 --- a/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/handleJoinPointMatch/resourceProxyExistsInRequestChain.js +++ b/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/handleJoinPointMatch/resourceProxyExistsInRequestChain.js @@ -4,12 +4,18 @@ const resourceProxyExistsInRequestChain = ({ proxyResource }) => { const queue = [issuerModule]; + const visited = new Set(); while (queue.length) { const node = queue.shift(); if (node.resource === proxyResource) { return true; } + if (visited.has(node)) { + continue; + } + visited.add(node); + const incomingConnections = moduleGraph.getIncomingConnections(node); queue.push( ...new Set( diff --git a/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/handleJoinPointMatch/resourceProxyExistsInRequestChain.test.js b/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/handleJoinPointMatch/resourceProxyExistsInRequestChain.test.js index 058a137..de74b7a 100644 --- a/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/handleJoinPointMatch/resourceProxyExistsInRequestChain.test.js +++ b/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/handleJoinPointMatch/resourceProxyExistsInRequestChain.test.js @@ -1,13 +1,14 @@ import resourceProxyExistsInRequestChain from "./resourceProxyExistsInRequestChain"; import { createMockGraph } from "../../../../../test/test-utils"; -const moduleGraph = { getIncomingConnections: jest.fn() }; const proxyResource = Symbol("test-proxy-resource"); describe("resourceProxyExistsInRequestChain", () => { let result; describe("when the issuer module is the proxy resource", () => { + const moduleGraph = { getIncomingConnections: jest.fn() }; + beforeEach(() => { result = resourceProxyExistsInRequestChain({ moduleGraph, @@ -118,28 +119,54 @@ describe("resourceProxyExistsInRequestChain", () => { }); describe("and one of the modules that imported the issuer module was not imported by the proxy resource", () => { - let calls; - - beforeEach(() => { - result = resourceProxyExistsInRequestChain({ - moduleGraph, - issuerModule, - proxyResource + describe("and there is a circular dependency", () => { + beforeEach(() => { + let count = 0; + const newModuleGraph = { + ...moduleGraph, + getIncomingConnections: jest.fn().mockImplementation((module) => { + if (count++ === 10) { + count = 0; + return moduleGraph.getIncomingConnections(issuerModule); + } + return moduleGraph.getIncomingConnections(module); + }) + }; + result = resourceProxyExistsInRequestChain({ + moduleGraph: newModuleGraph, + issuerModule, + proxyResource + }); }); - calls = moduleGraph.getIncomingConnections.mock.calls; + it("should return false, without locking up / running forever", () => { + expect(result).toBe(false); + }); }); - it("should have traversed the whole import tree of the issuer module", () => { - let expectedCount = 1; - for (let level = 1; level <= depth; level++) { - expectedCount += Math.pow(siblingsAtEachDepthCount, level); - } - expect(calls.length).toEqual(expectedCount); - }); + describe("and there is no circular dependency", () => { + beforeEach(() => { + result = resourceProxyExistsInRequestChain({ + moduleGraph, + issuerModule, + proxyResource + }); + }); + + it("should have traversed the whole import tree of the issuer module", () => { + let expectedCount = 1; + for (let level = 1; level <= depth; level++) { + expectedCount += Math.pow(siblingsAtEachDepthCount, level); + } - it("should return false", () => { - expect(result).toBe(false); + expect( + moduleGraph.getIncomingConnections.mock.calls.length + ).toEqual(expectedCount); + }); + + it("should return false", () => { + expect(result).toBe(false); + }); }); }); }); From d199c1cb2f3114d866855c7b8b1e0741bf9f8841 Mon Sep 17 00:00:00 2001 From: TomStrepsil <10725179+TomStrepsil@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:29:36 +0100 Subject: [PATCH 10/19] try being explicit about type import? --- test/automation/base.config.ts | 2 +- test/automation/docs/CHANGELOG.md | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/test/automation/base.config.ts b/test/automation/base.config.ts index 3fa6253..d5f9920 100644 --- a/test/automation/base.config.ts +++ b/test/automation/base.config.ts @@ -1,4 +1,4 @@ -import { devices, ReporterDescription } from "@playwright/test"; +import { devices, type ReporterDescription } from "@playwright/test"; const baseConfig = { fullyParallel: true, diff --git a/test/automation/docs/CHANGELOG.md b/test/automation/docs/CHANGELOG.md index 6a53fe4..2d6f487 100644 --- a/test/automation/docs/CHANGELOG.md +++ b/test/automation/docs/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.1.3] - 2025-08-19 + +- move to import type for `ReporterDescription` + ## [0.1.2] - 2024-12-18 ### Fixed From bad0091a4ad8967a59a13430cf529e6dbd51b0f0 Mon Sep 17 00:00:00 2001 From: TomStrepsil <10725179+TomStrepsil@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:49:19 +0100 Subject: [PATCH 11/19] import playwright types explicitly --- examples/express/docs/CHANGELOG.md | 1 + examples/express/playwright.config.ts | 2 +- examples/next/docs/CHANGELOG.md | 4 ++++ examples/next/playwright.config.ts | 2 +- examples/serve/docs/CHANGELOG.md | 4 ++++ examples/serve/playwright.config.ts | 2 +- 6 files changed, 12 insertions(+), 3 deletions(-) diff --git a/examples/express/docs/CHANGELOG.md b/examples/express/docs/CHANGELOG.md index 03a6019..cfcccb5 100644 --- a/examples/express/docs/CHANGELOG.md +++ b/examples/express/docs/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - removed "Vary" header from "animals" example, the page is meant to be un-cacheable, and the value was wrong in any case +- import types explicitly from `@playwright/test` after unexpected pipeline failure ## [0.2.5] - 2025-05-27 diff --git a/examples/express/playwright.config.ts b/examples/express/playwright.config.ts index 0288d5e..c0e2fbd 100644 --- a/examples/express/playwright.config.ts +++ b/examples/express/playwright.config.ts @@ -1,4 +1,4 @@ -import { defineConfig, PlaywrightTestConfig } from "@playwright/test"; +import { defineConfig, type PlaywrightTestConfig } from "@playwright/test"; import baseConfig from "../../test/automation/base.config"; const config: PlaywrightTestConfig = { diff --git a/examples/next/docs/CHANGELOG.md b/examples/next/docs/CHANGELOG.md index 08af4aa..84cc6eb 100644 --- a/examples/next/docs/CHANGELOG.md +++ b/examples/next/docs/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - update to take supply static `webpackNormalModule` corresponding to webpack plugin [version 0.9.0](../../../packages/webpack/docs/CHANGELOG.md#090---2025-07-29) +### Fixed + +- import types explicitly from `@playwright/test` after unexpected pipeline failure + ## [0.2.4] - 2025-05-27 ### Changed diff --git a/examples/next/playwright.config.ts b/examples/next/playwright.config.ts index 2f163c0..88d6b5e 100644 --- a/examples/next/playwright.config.ts +++ b/examples/next/playwright.config.ts @@ -1,4 +1,4 @@ -import { defineConfig, PlaywrightTestConfig } from "@playwright/test"; +import { defineConfig, type PlaywrightTestConfig } from "@playwright/test"; import baseConfig from "../../test/automation/base.config"; const THREE_MINUTES = 3 * 60 * 1000; diff --git a/examples/serve/docs/CHANGELOG.md b/examples/serve/docs/CHANGELOG.md index 17cf9f2..62bba3b 100644 --- a/examples/serve/docs/CHANGELOG.md +++ b/examples/serve/docs/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - updated toggle handlers to take a `variantPathMap` corresponding to webpack [version 0.9.0](../../../packages/webpack/docs/CHANGELOG.md#090---2025-07-29) +### Fixed + +- import types explicitly from `@playwright/test` after unexpected pipeline failure + ## [0.2.6] - 2025-07-14 ### Changed diff --git a/examples/serve/playwright.config.ts b/examples/serve/playwright.config.ts index c0a196a..1b73718 100644 --- a/examples/serve/playwright.config.ts +++ b/examples/serve/playwright.config.ts @@ -1,4 +1,4 @@ -import { defineConfig, PlaywrightTestConfig } from "@playwright/test"; +import { defineConfig, type PlaywrightTestConfig } from "@playwright/test"; import baseConfig from "../../test/automation/base.config"; const config: PlaywrightTestConfig = { From 334d8f937de786b78a5a5c7e42a95e9f5316dfd1 Mon Sep 17 00:00:00 2001 From: TomStrepsil <10725179+TomStrepsil@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:56:16 +0100 Subject: [PATCH 12/19] more type explicitness --- examples/next/docs/CHANGELOG.md | 2 +- .../app/fixtures/experiments/playwright.setExperimentHeaders.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/next/docs/CHANGELOG.md b/examples/next/docs/CHANGELOG.md index 84cc6eb..be1aa72 100644 --- a/examples/next/docs/CHANGELOG.md +++ b/examples/next/docs/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- import types explicitly from `@playwright/test` after unexpected pipeline failure +- import types explicitly from `@playwright/test` & internally, after unexpected pipeline failure ## [0.2.4] - 2025-05-27 diff --git a/examples/next/src/app/fixtures/experiments/playwright.setExperimentHeaders.ts b/examples/next/src/app/fixtures/experiments/playwright.setExperimentHeaders.ts index 20e0c84..1111f8f 100644 --- a/examples/next/src/app/fixtures/experiments/playwright.setExperimentHeaders.ts +++ b/examples/next/src/app/fixtures/experiments/playwright.setExperimentHeaders.ts @@ -6,7 +6,7 @@ import { PlaywrightWorkerOptions } from "@playwright/test"; -import { Experiments, Decision } from "./experiments"; +import type { Experiments, Decision } from "./experiments"; const setExperimentHeaders = ( test: TestType< From 4eba157183d13e8582c178ec31995f7c57db41ad Mon Sep 17 00:00:00 2001 From: TomStrepsil <10725179+TomStrepsil@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:10:36 +0100 Subject: [PATCH 13/19] more explicit type imports --- examples/next/src/app/fixtures/experiments/getExperiments.ts | 2 +- examples/next/src/app/fixtures/experiments/layout.tsx | 2 +- .../src/app/fixtures/experiments/playwright.locateInExample.ts | 2 +- .../app/fixtures/experiments/playwright.setExperimentHeaders.ts | 2 +- examples/next/src/app/layout.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/next/src/app/fixtures/experiments/getExperiments.ts b/examples/next/src/app/fixtures/experiments/getExperiments.ts index fc2f1ab..80450be 100644 --- a/examples/next/src/app/fixtures/experiments/getExperiments.ts +++ b/examples/next/src/app/fixtures/experiments/getExperiments.ts @@ -1,5 +1,5 @@ import { headers } from "next/headers"; -import { Experiments } from "./experiments"; +import type { Experiments } from "./experiments"; export default async function getExperiments(): Promise< Experiments | Record diff --git a/examples/next/src/app/fixtures/experiments/layout.tsx b/examples/next/src/app/fixtures/experiments/layout.tsx index 7b23ca7..0fcf7c2 100644 --- a/examples/next/src/app/fixtures/experiments/layout.tsx +++ b/examples/next/src/app/fixtures/experiments/layout.tsx @@ -1,4 +1,4 @@ -import { ReactNode } from "react"; +import type { ReactNode } from "react"; import getExperiments from "./getExperiments"; import Example from "./example"; diff --git a/examples/next/src/app/fixtures/experiments/playwright.locateInExample.ts b/examples/next/src/app/fixtures/experiments/playwright.locateInExample.ts index 32224e8..970c9d2 100644 --- a/examples/next/src/app/fixtures/experiments/playwright.locateInExample.ts +++ b/examples/next/src/app/fixtures/experiments/playwright.locateInExample.ts @@ -1,4 +1,4 @@ -import { Page, Locator } from "@playwright/test"; +import type { Page, Locator } from "@playwright/test"; function locateWithinExample(page: Page, testId: string): Locator { return page.locator("#example").getByTestId(testId); diff --git a/examples/next/src/app/fixtures/experiments/playwright.setExperimentHeaders.ts b/examples/next/src/app/fixtures/experiments/playwright.setExperimentHeaders.ts index 1111f8f..4b16956 100644 --- a/examples/next/src/app/fixtures/experiments/playwright.setExperimentHeaders.ts +++ b/examples/next/src/app/fixtures/experiments/playwright.setExperimentHeaders.ts @@ -1,4 +1,4 @@ -import { +import type { TestType, PlaywrightTestArgs, PlaywrightTestOptions, diff --git a/examples/next/src/app/layout.tsx b/examples/next/src/app/layout.tsx index bcc6be7..0b642db 100644 --- a/examples/next/src/app/layout.tsx +++ b/examples/next/src/app/layout.tsx @@ -1,6 +1,6 @@ import { Didact_Gothic } from "next/font/google"; import styles from "./styles.module.css"; -import { ReactNode } from "react"; +import type { ReactNode } from "react"; const didactGothic = Didact_Gothic({ display: "swap", From 7c71a5a96e5fdd37285fc8a6b0b3958ce1945292 Mon Sep 17 00:00:00 2001 From: TomStrepsil <10725179+TomStrepsil@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:15:07 +0100 Subject: [PATCH 14/19] sigh... more types --- .../fixtures/experiments/1-varied-component/playwright.spec.ts | 2 +- .../fixtures/experiments/4-varied-variant/playwright.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/next/src/app/fixtures/experiments/1-varied-component/playwright.spec.ts b/examples/next/src/app/fixtures/experiments/1-varied-component/playwright.spec.ts index 2350b62..e61d32d 100644 --- a/examples/next/src/app/fixtures/experiments/1-varied-component/playwright.spec.ts +++ b/examples/next/src/app/fixtures/experiments/1-varied-component/playwright.spec.ts @@ -1,4 +1,4 @@ -import { test, expect, ConsoleMessage } from "@playwright/test"; +import { test, expect, type ConsoleMessage } from "@playwright/test"; import setExperimentHeaders from "../playwright.setExperimentHeaders"; import locateWithinExample from "../playwright.locateInExample"; import getFixtureURL from "../playwright.getFixtureUrl"; diff --git a/examples/next/src/app/fixtures/experiments/4-varied-variant/playwright.spec.ts b/examples/next/src/app/fixtures/experiments/4-varied-variant/playwright.spec.ts index 7e02386..e4b4fc4 100644 --- a/examples/next/src/app/fixtures/experiments/4-varied-variant/playwright.spec.ts +++ b/examples/next/src/app/fixtures/experiments/4-varied-variant/playwright.spec.ts @@ -1,4 +1,4 @@ -import { test, expect, ConsoleMessage } from "@playwright/test"; +import { test, expect, type ConsoleMessage } from "@playwright/test"; import setExperimentHeaders from "../playwright.setExperimentHeaders"; import locateWithinExample from "../playwright.locateInExample"; import getFixtureURL from "../playwright.getFixtureUrl"; From 955080fc5334a9b3e680de53559a65e96bc66d22 Mon Sep 17 00:00:00 2001 From: TomStrepsil <10725179+TomStrepsil@users.noreply.github.com> Date: Thu, 21 Aug 2025 18:42:50 +0100 Subject: [PATCH 15/19] version & changelog for test/automation --- test/automation/docs/CHANGELOG.md | 2 ++ test/automation/package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/automation/docs/CHANGELOG.md b/test/automation/docs/CHANGELOG.md index 2d6f487..add0c91 100644 --- a/test/automation/docs/CHANGELOG.md +++ b/test/automation/docs/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.1.3] - 2025-08-19 +## Changed + - move to import type for `ReporterDescription` ## [0.1.2] - 2024-12-18 diff --git a/test/automation/package.json b/test/automation/package.json index 4798d47..70dd3cb 100644 --- a/test/automation/package.json +++ b/test/automation/package.json @@ -1,6 +1,6 @@ { "name": "web-toggle-point-automation-tests", - "version": "0.1.2", + "version": "0.1.3", "main": "index.js", "keywords": [], "type": "module", From a133186f915acab4c4d8be226128fe538f390b6b Mon Sep 17 00:00:00 2001 From: TomStrepsil <10725179+TomStrepsil@users.noreply.github.com> Date: Thu, 21 Aug 2025 23:07:25 +0100 Subject: [PATCH 16/19] try/catch resolution --- packages/webpack/docs/CHANGELOG.md | 1 + .../resolveJoinPoints/index.js | 24 ++++---- .../resolveJoinPoints/index.test.js | 59 ++++++++----------- 3 files changed, 37 insertions(+), 47 deletions(-) diff --git a/packages/webpack/docs/CHANGELOG.md b/packages/webpack/docs/CHANGELOG.md index 18853e4..8be9ac5 100644 --- a/packages/webpack/docs/CHANGELOG.md +++ b/packages/webpack/docs/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - removed "next" peer dependency, this needn't be explicit - ensured files that cannot be resolved (by [enhanced-resolve](https://github.com/webpack/enhanced-resolve/)), for whatever reason, don't break the build +- don't try and filter potential resolutions, let enhanced-resolve try and potentially fail, to allow for resolve plugins to have irregular specifiers (e.g. path alias') - ensured that circular dependencies don't cause the module graph search lock up ## [0.8.0] - 2025-05-27 diff --git a/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/index.js b/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/index.js index 40e24f2..21f3fb8 100644 --- a/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/index.js +++ b/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/index.js @@ -4,9 +4,6 @@ import { promisify } from "util"; import handleJoinPointMatch from "./handleJoinPointMatch"; const { relative } = posix; -const isLoaderlessFileRequest = (request) => - [".", "/"].includes(request.at(0)) && !request.includes("!"); - const matchJoinPointIfResolved = async ({ enhancedResolve, resolveData, @@ -14,12 +11,18 @@ const matchJoinPointIfResolved = async ({ joinPointFiles, compilation }) => { - const resolved = await enhancedResolve( - {}, - resolveData.context, - resolveData.request, - {} - ); + let resolved; + try { + resolved = await enhancedResolve( + {}, + resolveData.context, + resolveData.request, + {} + ); + } catch { + return; + } + if (!resolved) { return; } @@ -50,8 +53,7 @@ const resolveJoinPoints = ({ async (resolveData) => { if ( !joinPointFiles.size || - !resolveData.context.replaceAll(sep, posix.sep).startsWith(appRoot) || - !isLoaderlessFileRequest(resolveData.request) + !resolveData.context.replaceAll(sep, posix.sep).startsWith(appRoot) ) { return; } diff --git a/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/index.test.js b/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/index.test.js index b08d897..36a9c81 100644 --- a/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/index.test.js +++ b/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/index.test.js @@ -109,53 +109,40 @@ describe("resolveJoinPoints", () => { }); }); - describe("and the request is for a file with a webpack loader", () => { - beforeEach(() => { - resolveData = { - context: appRoot.replaceAll("/", sep), - request: "test-loader!test-request" - }; - beforeResolveCallback(resolveData); + const makeCommonAssertions = () => { + it("should resolve the request to a module file", () => { + expect(mockResolve).toHaveBeenCalledWith( + {}, + resolveData.context, + resolveData.request, + {}, + expect.any(Function) + ); }); + }; - it("should not try to handle a match", () => { - expect(handleJoinPointMatch).not.toHaveBeenCalled(); - }); - }); - - describe("and the request is for a module rather than a file", () => { + describe("and the request is for a file within the app root", () => { beforeEach(() => { resolveData = { context: appRoot.replaceAll("/", sep), - request: "test-request" + request: "/test-request" }; - beforeResolveCallback(resolveData); }); - it("should not try to handle a match", () => { - expect(handleJoinPointMatch).not.toHaveBeenCalled(); - }); - }); + describe("and an error is thrown whilst trying to resolve", () => { + beforeEach(() => { + mockResolve.mockImplementationOnce(() => { + throw new Error("Test error"); + }); + beforeResolveCallback(resolveData); + }); - describe("and the request is for a file within the app root, without any loaders", () => { - beforeEach(() => { - resolveData = { - context: appRoot.replaceAll("/", sep), - request: "/test-request" - }; - }); + makeCommonAssertions(); - const makeCommonAssertions = () => { - it("should resolve the request to a module file", () => { - expect(mockResolve).toHaveBeenCalledWith( - {}, - resolveData.context, - resolveData.request, - {}, - expect.any(Function) - ); + it("should not try to handle a match", () => { + expect(handleJoinPointMatch).not.toHaveBeenCalled(); }); - }; + }); describe("and the file cannot be resolved", () => { beforeEach(() => { From b3eddac4ade207dc890883e35bed4311a52c3840 Mon Sep 17 00:00:00 2001 From: TomStrepsil <10725179+TomStrepsil@users.noreply.github.com> Date: Sun, 24 Aug 2025 08:56:53 +0100 Subject: [PATCH 17/19] Set -> WeakSet --- package-lock.json | 2 +- .../handleJoinPointMatch/resourceProxyExistsInRequestChain.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3929385..db997fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20008,7 +20008,7 @@ }, "test/automation": { "name": "web-toggle-point-automation-tests", - "version": "0.1.2", + "version": "0.1.3", "devDependencies": { "@playwright/test": "^1.49.0", "@types/node": "^22.9.1", diff --git a/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/handleJoinPointMatch/resourceProxyExistsInRequestChain.js b/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/handleJoinPointMatch/resourceProxyExistsInRequestChain.js index bfc049c..d6a9868 100644 --- a/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/handleJoinPointMatch/resourceProxyExistsInRequestChain.js +++ b/packages/webpack/src/plugins/togglePointInjection/resolveJoinPoints/handleJoinPointMatch/resourceProxyExistsInRequestChain.js @@ -4,7 +4,7 @@ const resourceProxyExistsInRequestChain = ({ proxyResource }) => { const queue = [issuerModule]; - const visited = new Set(); + const visited = new WeakSet(); while (queue.length) { const node = queue.shift(); if (node.resource === proxyResource) { From c1357208c4e91e92924042fac0a493b222f28c0d Mon Sep 17 00:00:00 2001 From: TomStrepsil <10725179+TomStrepsil@users.noreply.github.com> Date: Sun, 31 Aug 2025 17:30:23 +0100 Subject: [PATCH 18/19] correct documentation re: toggleHandler --- packages/webpack/docs/CHANGELOG.md | 1 + packages/webpack/docs/README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/webpack/docs/CHANGELOG.md b/packages/webpack/docs/CHANGELOG.md index 8be9ac5..7488454 100644 --- a/packages/webpack/docs/CHANGELOG.md +++ b/packages/webpack/docs/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ensured files that cannot be resolved (by [enhanced-resolve](https://github.com/webpack/enhanced-resolve/)), for whatever reason, don't break the build - don't try and filter potential resolutions, let enhanced-resolve try and potentially fail, to allow for resolve plugins to have irregular specifiers (e.g. path alias') - ensured that circular dependencies don't cause the module graph search lock up +- correct README.md to show `toggleHandler` as an option of the `pointCut`, not the general plugin configuration ## [0.8.0] - 2025-05-27 diff --git a/packages/webpack/docs/README.md b/packages/webpack/docs/README.md index a262523..f583514 100644 --- a/packages/webpack/docs/README.md +++ b/packages/webpack/docs/README.md @@ -39,11 +39,11 @@ interface PointCut { togglePointModule: string; variantGlobs?: string[]; joinPointResolver?: (variantPath: string) => string; + toggleHandler?: string; } interface TogglePointInjectionOptions { pointCuts: PointCut[]; - toggleHandler?: string; webpackNormalModule?: () => typeof NormalModule; } ``` From 83739704ffc55036550365c862f8903e9df4a747 Mon Sep 17 00:00:00 2001 From: TomStrepsil <10725179+TomStrepsil@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:38:16 +0100 Subject: [PATCH 19/19] type fix now part of #43 --- examples/express/docs/CHANGELOG.md | 1 - examples/next/docs/CHANGELOG.md | 4 ---- examples/serve/docs/CHANGELOG.md | 4 ---- 3 files changed, 9 deletions(-) diff --git a/examples/express/docs/CHANGELOG.md b/examples/express/docs/CHANGELOG.md index cfcccb5..03a6019 100644 --- a/examples/express/docs/CHANGELOG.md +++ b/examples/express/docs/CHANGELOG.md @@ -10,7 +10,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - removed "Vary" header from "animals" example, the page is meant to be un-cacheable, and the value was wrong in any case -- import types explicitly from `@playwright/test` after unexpected pipeline failure ## [0.2.5] - 2025-05-27 diff --git a/examples/next/docs/CHANGELOG.md b/examples/next/docs/CHANGELOG.md index be1aa72..08af4aa 100644 --- a/examples/next/docs/CHANGELOG.md +++ b/examples/next/docs/CHANGELOG.md @@ -11,10 +11,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - update to take supply static `webpackNormalModule` corresponding to webpack plugin [version 0.9.0](../../../packages/webpack/docs/CHANGELOG.md#090---2025-07-29) -### Fixed - -- import types explicitly from `@playwright/test` & internally, after unexpected pipeline failure - ## [0.2.4] - 2025-05-27 ### Changed diff --git a/examples/serve/docs/CHANGELOG.md b/examples/serve/docs/CHANGELOG.md index 62bba3b..17cf9f2 100644 --- a/examples/serve/docs/CHANGELOG.md +++ b/examples/serve/docs/CHANGELOG.md @@ -11,10 +11,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - updated toggle handlers to take a `variantPathMap` corresponding to webpack [version 0.9.0](../../../packages/webpack/docs/CHANGELOG.md#090---2025-07-29) -### Fixed - -- import types explicitly from `@playwright/test` after unexpected pipeline failure - ## [0.2.6] - 2025-07-14 ### Changed