diff --git a/__tests__/spec/sanity-checks.js b/__tests__/spec/sanity-checks.js index 3bef0bb81a..d1c605d20c 100644 --- a/__tests__/spec/sanity-checks.js +++ b/__tests__/spec/sanity-checks.js @@ -1,7 +1,6 @@ /* eslint-env jest */ // core dependencies -const assert = require('assert') const path = require('path') // npm dependencies @@ -69,78 +68,4 @@ describe('The Prototype Kit', () => { expect(response.type).toBe('text/html') }) }) - - describe('plugins', () => { - it('should allow known assets to be loaded from node_modules', (done) => { - request(app) - .get('/plugin-assets/govuk-frontend/govuk/all.js') - .expect(200) - .expect('Content-Type', /application\/javascript; charset=UTF-8/) - .end((err, res) => { - if (err) { - done(err) - } else { - assert.strictEqual( - '' + res.text, - fse.readFileSync(path.join(projectDir, 'node_modules', 'govuk-frontend', 'govuk', 'all.js'), 'utf8') - ) - done() - } - }) - }) - - it('should allow known assets to be loaded from node_modules', (done) => { - request(app) - .get('/plugin-assets/govuk-frontend/govuk/assets/images/favicon.ico') - .expect(200) - .expect('Content-Type', /image\/x-icon/) - .end((err, res) => { - if (err) { - done(err) - } else { - assert.strictEqual( - '' + res.body, - fse.readFileSync(path.join(projectDir, 'node_modules', 'govuk-frontend', 'govuk', 'assets', 'images', 'favicon.ico'), 'utf8') - ) - done() - } - }) - }) - - it('should not expose everything', (done) => { - const consoleErrorMock = jest.spyOn(global.console, 'error').mockImplementation() - - request(app) - .get('/govuk/assets/common.js') - .expect(404) - .end((err, res) => { - consoleErrorMock.mockRestore() - if (err) { - done(err) - } else { - done() - } - }) - }) - - describe('misconfigured prototype kit - while updating kit developer did not copy over changes in /app folder', () => { - it('should still allow known assets to be loaded from node_modules', (done) => { - request(app) - .get('/plugin-assets/govuk-frontend/govuk/all.js') - .expect(200) - .expect('Content-Type', /application\/javascript; charset=UTF-8/) - .end((err, res) => { - if (err) { - done(err) - } else { - assert.strictEqual( - '' + res.text, - fse.readFileSync(path.join(projectDir, 'node_modules', 'govuk-frontend', 'govuk', 'all.js'), 'utf8') - ) - done() - } - }) - }) - }) - }) }) diff --git a/lib/authentication.js b/lib/authentication.js index 03d0beec0e..e4a43d7754 100644 --- a/lib/authentication.js +++ b/lib/authentication.js @@ -1,4 +1,3 @@ - // core dependencies const url = require('url') @@ -14,8 +13,7 @@ const allowedPathsWhenUnauthenticated = [ '/plugin-assets/govuk-frontend/govuk/all.js', // Keep /extension-assets path for backwards compatibility // TODO: remove in v14 - '/extension-assets/govuk-prototype-kit/lib/assets/images/unbranded.ico', - '/extension-assets/govuk-frontend/govuk/all.js'] + '/extension-assets/govuk-prototype-kit/lib/assets/images/unbranded.ico'] function authentication () { if (!shouldUseAuth()) { @@ -38,7 +36,11 @@ function authentication () { const password = encryptPassword(config.getConfig().password) return (req, res, next) => { - if (allowedPathsWhenUnauthenticated.includes(req.path)) { + if (allowedPathsWhenUnauthenticated.includes(req.path) || + req.path.startsWith('/manage-prototype/dependencies') || + req.path.startsWith('/plugin-assets/govuk-prototype-kit') || + req.path === '/public/stylesheets/manage-prototype.css' + ) { next() } else if (isAuthenticated(password, req)) { next() diff --git a/lib/build.js b/lib/build.js index dbf8714066..667a2a81fc 100644 --- a/lib/build.js +++ b/lib/build.js @@ -19,10 +19,9 @@ const { publicCssDir, shadowNunjucksDir, backupNunjucksDir, - appViewsDir, - packageDir + appViewsDir } = require('./utils/paths') -const { recursiveDirectoryContentsSync } = require('./utils') +const { recursiveDirectoryContentsSync, getInternalGovukFrontendDir } = require('./utils') const { startPerformanceTimer, endPerformanceTimer } = require('./utils/performance') const { verboseLog } = require('./utils/verboseLogger') const { hasRestartedAfterError } = require('./sync-changes') @@ -84,21 +83,13 @@ function ensureTempDirExists (dir = tmpDir) { fse.writeFileSync(path.join(tmpDir, '.gitignore'), '*') } -function getInternalGovUkFrontendDir () { - let internalGovUkFrontendDir = path.join(packageDir, 'node_modules', 'govuk-frontend') - if (!fse.pathExistsSync(internalGovUkFrontendDir)) { - internalGovUkFrontendDir = path.join(projectDir, 'node_modules', 'govuk-frontend') - } - return internalGovUkFrontendDir -} - function sassInclude (filePath) { return `@import "${filePath.split(path.sep).join('/')}";` } function sassKitFrontendDependency () { const timer = startPerformanceTimer() - const internalGovUkFrontendDir = getInternalGovUkFrontendDir() + const internalGovUkFrontendDir = getInternalGovukFrontendDir() const internalGovUkFrontendConfig = fse.readJsonSync(path.join(internalGovUkFrontendDir, 'govuk-prototype-kit.config.json')) const fileContents = internalGovUkFrontendConfig.sass .map(sassPath => path.join(internalGovUkFrontendDir, sassPath)) diff --git a/lib/errorServer.js b/lib/errorServer.js index 6287aadb2d..b47a96b0ef 100644 --- a/lib/errorServer.js +++ b/lib/errorServer.js @@ -7,6 +7,7 @@ const { verboseLog } = require('./utils/verboseLogger') const syncChanges = require('./sync-changes') const { flagError } = require('./sync-changes') const { packageDir } = require('./utils/paths') +const { getInternalGovukFrontendDir } = require('./utils') function runErrorServer (error) { flagError(error) @@ -52,11 +53,23 @@ function runErrorServer (error) { } if (req.url.startsWith('/plugin-assets')) { res.setHeader('Content-Type', fileExtensionsToMimeTypes[req.url.split('.').at(-1)] || 'text/plain') - const urlParts = req.url.split('/') - urlParts.shift() - urlParts[0] = 'node_modules' - const filePath = path.join(process.cwd(), ...urlParts) - res.end(fs.readFileSync(filePath)) + const urlParts = req.url.split('/').slice(2) + const pluginName = urlParts.shift() + let filePath + if (pluginName === 'govuk-frontend') { + filePath = path.join(getInternalGovukFrontendDir(), ...urlParts) + } else { + filePath = path.join(process.cwd(), 'node_modules', pluginName, ...urlParts) + } + try { + const contents = fs.readFileSync(filePath) + res.writeHead(200) + res.end(contents) + } catch (e) { + console.log('Couldn\'t load url in error server: ', req.url) + res.writeHead(500) + res.end('500 Server Error') + } return } diff --git a/lib/manage-prototype-handlers.js b/lib/manage-prototype-handlers.js index 0212c0ba64..b6defcc838 100644 --- a/lib/manage-prototype-handlers.js +++ b/lib/manage-prototype-handlers.js @@ -122,7 +122,7 @@ function postPasswordHandler (req, res) { // Middleware to ensure the routes specified below will render the manage-prototype-not-available // view when the prototype is not running in development function developmentOnlyMiddleware (req, res, next) { - if (config.getConfig().isDevelopment) { + if (config.getConfig().isDevelopment || req.url.startsWith('/dependencies/govuk-frontend')) { next() } else { res.render(getManagementView('manage-prototype-not-available.njk')) @@ -131,7 +131,10 @@ function developmentOnlyMiddleware (req, res, next) { // Middleware to ensure pages load when plugin cache has been initially loaded async function pluginCacheMiddleware (req, res, next) { - await waitForPackagesCache() + await Promise.race([ + waitForPackagesCache(), + new Promise((resolve) => setTimeout(resolve, 1000)) + ]) next() } diff --git a/lib/manage-prototype-handlers.test.js b/lib/manage-prototype-handlers.test.js index b3ea81bdf7..b2b3b010b6 100644 --- a/lib/manage-prototype-handlers.test.js +++ b/lib/manage-prototype-handlers.test.js @@ -133,7 +133,8 @@ describe('manage-prototype-handlers', () => { query: {}, params: {}, route: {}, - originalUrl: '/current-url' + originalUrl: '/current-url', + url: '/current-url' } res = { render: jest.fn(), diff --git a/lib/manage-prototype-routes.js b/lib/manage-prototype-routes.js index e0d722be54..5ad9460d12 100644 --- a/lib/manage-prototype-routes.js +++ b/lib/manage-prototype-routes.js @@ -30,11 +30,6 @@ const path = require('path') const { getInternalGovukFrontendDir } = require('./utils') const router = require('../index').requests.setupRouter(contextPath) -const redirectingRouter = require('../index').requests.setupRouter('/manage-prototype') - -redirectingRouter.use((req, res) => { - res.redirect(req.originalUrl.replace('/manage-prototype', contextPath)) -}) router.get('/csrf-token', getCsrfTokenHandler) @@ -84,7 +79,14 @@ router.post('/plugins/:mode', postPluginsModeMiddleware) router.post('/plugins/:mode', csrfProtection, postPluginsModeHandler) -router.use('/dependencies/govuk-frontend/govuk/assets', express.static(path.join(getInternalGovukFrontendDir(), 'govuk', 'assets'))) +const partialGovukFrontendUrls = [ + 'govuk/assets', + 'govuk/all.js', + 'govuk-prototype-kit/init.js' +] +partialGovukFrontendUrls.forEach(url => { + router.use(`/dependencies/govuk-frontend/${url}`, express.static(path.join(getInternalGovukFrontendDir(), url))) +}) setKitRestarted(true) diff --git a/lib/nunjucks/views/error-handling/page-not-found.njk b/lib/nunjucks/views/error-handling/page-not-found.njk index 44c3a4fe5a..25617cae87 100644 --- a/lib/nunjucks/views/error-handling/page-not-found.njk +++ b/lib/nunjucks/views/error-handling/page-not-found.njk @@ -1,4 +1,4 @@ -{% extends "govuk-prototype-kit/layouts/govuk-branded.njk" %} +{% extends "views/manage-prototype/layout.njk" %} {% block pageTitle %} Page not found – {{ serviceName }} – GOV.UK Prototype Kit diff --git a/lib/nunjucks/views/manage-prototype/layout.njk b/lib/nunjucks/views/manage-prototype/layout.njk index bcaf24ff0a..78d03e520d 100644 --- a/lib/nunjucks/views/manage-prototype/layout.njk +++ b/lib/nunjucks/views/manage-prototype/layout.njk @@ -16,7 +16,12 @@ {% include "views/manage-prototype/stylesheets.njk" %} {% endblock %} -{% block beforeContent %} +{% block scripts %} + {% include "views/manage-prototype/scripts.njk" %} + {% block pageScripts %}{% endblock %} +{% endblock %} + +{% block beforeContent %}