11const webpack = require ( 'webpack' ) ;
22const path = require ( 'path' ) ;
3+ const fs = require ( 'node:fs' ) ;
34const buildSingleVersion = process . env . BUILD_SINGLE_VERSION === 'true' ;
45
6+ // Ensure versions is always in GraphQL schema
7+ exports . createSchemaCustomization = ( { actions } ) => {
8+ const { createTypes } = actions ;
9+ const typeDefs = `
10+ type SiteSiteMetadata implements Node {
11+ versions: [String!]!
12+ }
13+ ` ;
14+ createTypes ( typeDefs ) ;
15+ } ;
16+
517exports . onCreateWebpackConfig = ( { actions, getConfig } ) => {
618 const config = getConfig ( ) ;
719
20+ // Custom resolver plugin to handle dynamic aliases for CSS imports
21+ // This hooks into webpack's resolver to catch all module resolutions including CSS
22+ class DynamicAliasResolverPlugin {
23+ apply ( resolver ) {
24+ // Hook into 'resolve' hook and run it with high priority (early in the chain)
25+ resolver . hooks . resolve . tapAsync ( {
26+ name : 'DynamicAliasResolverPlugin' ,
27+ stage : 1 , // Run early, before AliasPlugin (which runs at stage 10)
28+ } , ( request , resolveContext , callback ) => {
29+ // Handle ~hds-core imports (CSS/SCSS imports)
30+ if ( request && request . request && request . request . startsWith ( '~hds-core' ) ) {
31+ // Get context path - try multiple possible locations
32+ const contextPath = request . context ?. path ||
33+ request . path ||
34+ ( resolveContext && resolveContext . issuer ) ||
35+ '' ;
36+
37+ // Normalize path separators to handle Windows and POSIX paths uniformly
38+ const normalizedContextPath = contextPath . split ( path . sep ) . join ( '/' ) ;
39+
40+ // Extract version from context path
41+ // Use word boundary or path separator to ensure complete version matching
42+ const versionMatch = normalizedContextPath . match ( / (?: d o c s - r e l e a s e - | h e l s i n k i - d e s i g n - s y s t e m - | \. p r e v i o u s - v e r s i o n s \/ h e l s i n k i - d e s i g n - s y s t e m - ) ( \d + \. \d + \. \d + ) (?: \/ | $ ) / ) ;
43+ if ( versionMatch ) {
44+ const fullVersion = versionMatch [ 1 ] ;
45+ // Replace ~hds-core with hds-core-{version}
46+ const newRequest = request . request . replace ( '~hds-core' , `hds-core-${ fullVersion } ` ) ;
47+ const newRequestObj = {
48+ ...request ,
49+ request : newRequest
50+ } ;
51+ // Continue resolution with the modified request
52+ return resolver . doResolve ( resolver . hooks . resolve , newRequestObj , null , resolveContext , callback ) ;
53+ }
54+ }
55+ // Continue with normal resolution
56+ return callback ( ) ;
57+ } ) ;
58+ }
59+ }
60+
861 config . plugins . push (
962 new webpack . NormalModuleReplacementPlugin (
10- / h d s - c o r e | h d s - r e a c t / ,
63+ / ( ~ ? h d s - c o r e | h d s - r e a c t ) / ,
1164 resource => {
12- if ( resource . context . includes ( '.cache/gatsby-source-git/docs-release-2.' ) ) {
13- resource . request = resource . request . replace ( 'hds-core' , 'hds-2-core' ) ;
14- resource . request = resource . request . replace ( 'hds-react' , 'hds-2-react' ) ;
65+ // Skip if already versioned (prevent double replacement)
66+ // Check for pattern like hds-core-X.Y.Z or hds-react-X.Y.Z
67+ if ( resource . request . match ( / h d s - ( c o r e | r e a c t ) - \d + \. \d + \. \d + / ) ) {
68+ return ;
1569 }
16- if ( resource . context . includes ( '.cache/gatsby-source-git/docs-release-3.' ) ) {
17- resource . request = resource . request . replace ( 'hds-core' , 'hds-3-core' ) ;
18- resource . request = resource . request . replace ( 'hds-react' , 'hds-3-react' ) ;
70+
71+ // Dynamically extract full version from path
72+ // Match patterns like:
73+ // - docs-release-X.Y.Z (from sourceInstanceName)
74+ // - helsinki-design-system-X.Y.Z (from .previous-versions path)
75+ // - .previous-versions/helsinki-design-system-X.Y.Z (full path)
76+ // Normalize path separators to handle Windows and POSIX paths uniformly
77+ const normalizedContext = resource . context . split ( path . sep ) . join ( '/' ) ;
78+ // Use word boundary or path separator to ensure complete version matching
79+ const versionMatch = normalizedContext . match ( / (?: d o c s - r e l e a s e - | h e l s i n k i - d e s i g n - s y s t e m - | \. p r e v i o u s - v e r s i o n s \/ h e l s i n k i - d e s i g n - s y s t e m - ) ( \d + \. \d + \. \d + ) (?: \/ | $ ) / ) ;
80+ if ( versionMatch ) {
81+ const fullVersion = versionMatch [ 1 ] ;
82+
83+ // Replace ~hds-core with hds-core-{version} (remove ~)
84+ if ( resource . request . includes ( '~hds-core' ) ) {
85+ resource . request = resource . request . replaceAll ( '~hds-core' , `hds-core-${ fullVersion } ` ) ;
86+ }
87+ // Replace hds-core with hds-core-{version} (only if not already versioned)
88+ else if ( resource . request . includes ( 'hds-core' ) && ! resource . request . includes ( 'hds-core-' ) ) {
89+ resource . request = resource . request . replaceAll ( 'hds-core' , `hds-core-${ fullVersion } ` ) ;
90+ }
91+ // Replace hds-react with hds-react-{version} (only if not already versioned)
92+ if ( resource . request . includes ( 'hds-react' ) && ! resource . request . includes ( 'hds-react-' ) ) {
93+ resource . request = resource . request . replaceAll ( 'hds-react' , `hds-react-${ fullVersion } ` ) ;
94+ }
1995 }
2096 }
2197 )
2298 ) ;
2399
100+
24101 actions . setWebpackConfig ( {
25102 plugins : [
26103 // We need to provide a polyfill for react-live library to make it work with the latest Gatsby: https://webpack.js.org/blog/2020-10-10-webpack-5-release/#automatic-nodejs-polyfills-removed
@@ -32,10 +109,13 @@ exports.onCreateWebpackConfig = ({ actions, getConfig }) => {
32109 resolve : {
33110 alias : {
34111 fs$ : path . resolve ( __dirname , 'src/fs.js' ) ,
35- '~hds-core' : 'hds-2-core' ,
36112 'hds-react' : 'hds-react/lib' ,
37113 stream : false ,
38114 } ,
115+ plugins : [
116+ new DynamicAliasResolverPlugin ( ) ,
117+ ...( config . resolve . plugins || [ ] ) ,
118+ ] ,
39119 fallback : {
40120 crypto : require . resolve ( 'crypto-browserify' ) ,
41121 } ,
@@ -91,10 +171,9 @@ exports.createPages = async ({ actions, graphql }) => {
91171 parent {
92172 ... on File {
93173 relativePath
174+ absolutePath
94175 ${ ! buildSingleVersion ?
95- ` gitRemote {
96- ref
97- }` : `` }
176+ ` sourceInstanceName` : `` }
98177 }
99178 }
100179 }
@@ -180,30 +259,79 @@ exports.createPages = async ({ actions, graphql }) => {
180259
181260 // Create pages dynamically
182261 result . data . allMdx . edges . forEach ( ( { node } ) => {
183- const gitRemote = node . parent ?. gitRemote ?. ref ;
184- const pathWithVersion = path . join ( '/' , gitRemote || '' , node . frontmatter . slug ) ;
262+ const sourceInstanceName = node . parent ?. sourceInstanceName ;
263+ // Extract version from sourceInstanceName (filesystem source)
264+ const versionFromSource = sourceInstanceName ?. startsWith ( 'docs-release-' )
265+ ? sourceInstanceName . replace ( 'docs-release-' , '' )
266+ : null ;
267+ const versionRef = versionFromSource ? `release-${ versionFromSource } ` : null ;
268+ const pathWithVersion = path . join ( '/' , versionRef || '' , node . frontmatter . slug ) ;
185269
186270 try {
187271 const pageTemplate = require . resolve ( './src/components/ContentLayoutWrapper.js' ) ;
188- const contentPath = './src/docs/' + node . parent . relativePath . replace ( 'site/src/docs/' , '' ) ;
272+ // Handle relativePath for both versioned sources and current docs
273+ const rawRelativePath = node . parent ?. relativePath || '' ;
274+ const normalizedRelativePath = rawRelativePath . replaceAll ( '\\' , '/' ) ;
275+ const docsPrefix = 'site/src/docs/' ;
276+ const docsRelativePath = normalizedRelativePath . includes ( docsPrefix )
277+ ? normalizedRelativePath . substring ( normalizedRelativePath . indexOf ( docsPrefix ) + docsPrefix . length )
278+ : normalizedRelativePath ;
279+ const contentPath = path . posix . join ( './src/docs' , docsRelativePath ) ;
280+ const absoluteContentPath = path . resolve ( __dirname , contentPath ) ;
189281
190- console . log ( 'createPage() ' + ( gitRemote ? gitRemote : 'latest' ) + ' ' + contentPath ) ;
282+ console . log ( 'createPage() ' + ( versionRef ? versionRef : 'latest' ) + ' ' + contentPath ) ;
191283
192- const pageContent = gitRemote
193- ? require . resolve ( `./.cache/gatsby-source-git/docs-${ gitRemote } /${ node . parent . relativePath } ` )
194- : require . resolve ( contentPath ) ;
284+ const pageContent = versionRef
285+ ? ( ( ) => {
286+ // For filesystem sources, use the absolutePath directly
287+ const absolutePath = node . parent ?. absolutePath ;
288+ if ( absolutePath && fs . existsSync ( absolutePath ) ) {
289+ try {
290+ return require . resolve ( absolutePath ) ;
291+ } catch ( e ) {
292+ console . warn ( `Could not resolve absolute path ${ absolutePath } : ${ e . message } ` ) ;
293+ }
294+ }
295+ // Fallback: construct path from version and docsRelativePath
296+ const version = versionRef . replace ( 'release-' , '' ) ;
297+ // Use the already-normalized docsRelativePath instead of node.parent.relativePath
298+ // to avoid double path segments
299+ const localPath = path . resolve ( __dirname , `.previous-versions/helsinki-design-system-${ version } /site/src/docs/${ docsRelativePath } ` ) ;
300+
301+ if ( fs . existsSync ( localPath ) ) {
302+ try {
303+ return require . resolve ( localPath ) ;
304+ } catch ( e ) {
305+ console . warn ( `Could not resolve local path ${ localPath } : ${ e . message } ` ) ;
306+ }
307+ }
308+ // Last resort: try contentPath (for latest docs)
309+ try {
310+ return require . resolve ( absoluteContentPath ) ;
311+ } catch ( e ) {
312+ console . warn ( `Could not resolve any path for ${ sourceInstanceName } /${ node . parent . relativePath } : ${ e . message } ` ) ;
313+ throw e ;
314+ }
315+ } ) ( )
316+ : require . resolve ( absoluteContentPath ) ;
195317
196318 // filter out duplicate slug entries.
197319 const allPages = Object . values (
198320 Object . fromEntries (
199321 mdxPageData
200- . filter ( ( { node } ) => node ?. parent ?. gitRemote ?. ref === gitRemote )
322+ . filter ( ( { node } ) => {
323+ const nodeSourceInstanceName = node ?. parent ?. sourceInstanceName ;
324+ const nodeVersionRef = nodeSourceInstanceName ?. startsWith ( 'docs-release-' )
325+ ? `release-${ nodeSourceInstanceName . replace ( 'docs-release-' , '' ) } `
326+ : null ;
327+ return nodeVersionRef === versionRef ;
328+ } )
201329 . filter ( ( { node } ) => node . frontmatter . slug && node . frontmatter . navTitle )
202330 . map ( ( { node } ) => [ node . frontmatter . slug , { ...node . frontmatter , ...node . fields } ] )
203331 ) ,
204332 ) ;
205333
206- const currentMenuItem = resolveCurrentMenuItem ( gitRemote , uiMenuLinks , node . frontmatter . slug ) ;
334+ const currentMenuItem = resolveCurrentMenuItem ( versionRef , uiMenuLinks , node . frontmatter . slug ) ;
207335 const uiSubMenuLinks = getUiSubMenuLinks ( allPages , uiMenuLinks , currentMenuItem ) ;
208336
209337 createPage ( {
0 commit comments