1+ import { existsSync } from 'fs'
2+ import { dirname , join } from 'path'
3+ import { fileURLToPath } from 'url'
4+
15import { hasFormComponents , slugSchema } from '@defra/forms-model'
26import Boom from '@hapi/boom'
37import {
@@ -11,6 +15,9 @@ import vision from '@hapi/vision'
1115import { isEqual } from 'date-fns'
1216import Joi from 'joi'
1317import nunjucks , { type Environment } from 'nunjucks'
18+ import resolvePkg from 'resolve'
19+
20+ import { paths } from '../nunjucks/environment.js'
1421
1522import { PREVIEW_PATH_PREFIX } from '~/src/server/constants.js'
1623import {
@@ -65,6 +72,20 @@ import * as httpService from '~/src/server/services/httpService.js'
6572import { CacheService } from '~/src/server/services/index.js'
6673import { type Services } from '~/src/server/types.js'
6774
75+ function findPackageRoot ( ) {
76+ const currentFileName = fileURLToPath ( import . meta. url )
77+ const currentDirectoryName = dirname ( currentFileName )
78+
79+ let dir = currentDirectoryName
80+ while ( dir !== '/' ) {
81+ if ( existsSync ( join ( dir , 'package.json' ) ) ) {
82+ return dir
83+ }
84+ dir = dirname ( dir )
85+ }
86+
87+ throw new Error ( 'package.json not found in parent directories' )
88+ }
6889export interface PluginOptions {
6990 model ?: FormModel
7091 services ?: Services
@@ -73,6 +94,9 @@ export interface PluginOptions {
7394 viewPaths ?: string [ ]
7495 filters ?: Record < string , FilterFunction >
7596 pluginPath ?: string
97+ nunjucks : {
98+ paths : string [ ]
99+ }
76100 viewContext : {
77101 baseLayoutPath : string
78102 }
@@ -88,24 +112,25 @@ export const plugin = {
88112 services = defaultServices ,
89113 controllers,
90114 cacheName,
91- viewPaths,
92115 filters,
93- pluginPath = PLUGIN_PATH ,
116+ nunjucks : nunjucksOptions ,
94117 viewContext
95118 } = options
96119 const { formsService } = services
97120 const cacheService = new CacheService ( server , cacheName )
98121
99- // Paths array to tell `vision` and `nunjucks` where template files are stored.
100- // We need to include `VIEW_PATH` in addition the runtime path (node_modules)
101- // to keep the local tests working
102- const path = [ ` ${ pluginPath } / ${ VIEW_PATH } ` , VIEW_PATH ]
122+ const packageRoot = findPackageRoot ( )
123+ const govukFrontendPath = dirname (
124+ resolvePkg . sync ( 'govuk-frontend/package.json' )
125+ )
103126
104- // Include any additional user provided view paths so our internal views engine
105- // can find any files they provide from the consumer side if using custom `page.view`s
106- if ( Array . isArray ( viewPaths ) && viewPaths . length ) {
107- path . push ( ...viewPaths )
108- }
127+ const viewPathResolved = join ( packageRoot , VIEW_PATH )
128+
129+ const paths = [
130+ ...nunjucksOptions . paths ,
131+ viewPathResolved ,
132+ join ( govukFrontendPath , 'dist' )
133+ ]
109134
110135 await server . register ( {
111136 plugin : vision ,
@@ -131,10 +156,7 @@ export const plugin = {
131156 ) => {
132157 // Nunjucks also needs an additional path configuration
133158 // to use the templates and macros from `govuk-frontend`
134- const environment = nunjucks . configure ( [
135- ...path ,
136- 'node_modules/govuk-frontend/dist'
137- ] )
159+ const environment = nunjucks . configure ( paths )
138160
139161 // Applies custom filters and globals for nunjucks
140162 // that are required by the `forms-engine-plugin`
@@ -146,7 +168,7 @@ export const plugin = {
146168 }
147169 }
148170 } ,
149- path,
171+ path : paths ,
150172 // Provides global context used with all templates
151173 context
152174 }
0 commit comments