Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add missing mandatory file extensions for ESM JavaScript #2658

Merged
merged 4 commits into from
Jun 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 14 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

### New features

#### Help users navigate through pages with pagination

You can now use [pagination](https://design-system.service.gov.uk/components/pagination/) to help users navigate forwards and backwards through a series of pages. For example, in search results or guidance that's divided into multiple website pages.

This was added in [pull request #2610: Add pagination component](https://github.com/alphagov/govuk-frontend/pull/2610).

#### Check checkboxes by using the `values` Nunjucks option

When using the `govukCheckboxes` Nunjucks macro, you can now use the `values` option to indicate which checkboxes should be checked when the page loads.
Expand Down Expand Up @@ -44,13 +50,17 @@ We've deprecated the `govuk-header__link--service-name` class, and will remove i

This change was introduced in [pull request #2617: Do not make the service name in the header a link if no `serviceUrl` is provided](https://github.com/alphagov/govuk-frontend/pull/2617).

### New features
#### File extensions added for JavaScript ES Module imports

#### Help users navigate through pages with pagination
We have updated our component ES module JavaScript to include [missing file extensions](https://nodejs.org/api/esm.html#mandatory-file-extensions) not provided in release 4.1.0. If you have received an error similar to the one below, this fix should resolve the issue.

You can now use [pagination](https://design-system.service.gov.uk/components/pagination/) to help users navigate forwards and backwards through a series of pages. For example, in search results or guidance that's divided into multiple website pages.
```
Cannot find module '../node_modules/govuk-frontend/govuk-esm/common' imported from ../node_modules/govuk-frontend/govuk-esm/all.mjs
```

This was added in [pull request #2610: Add pagination component](https://github.com/alphagov/govuk-frontend/pull/2610).
You should not need to make any changes if you are successfully importing our JavaScript as ES modules with version 4.1.0, but there still might be config you can remove. For example, removing `fullySpecified: false` from your Webpack config file.

This change was introduced in [pull request #2658: Add missing mandatory file extensions for ESM JavaScript](https://github.com/alphagov/govuk-frontend/pull/2658)

### Fixes

Expand Down
2 changes: 1 addition & 1 deletion docs/contributing/coding-standards/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ When creating your component, you should create the following files in the compo

If your component uses JavaScript, you must also create the following files in the component’s folder:

- `_[component-name].js`
- `_[component-name].mjs`
- `_[component-name].test.js`

## Building your components
Expand Down
10 changes: 6 additions & 4 deletions docs/contributing/coding-standards/js.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ JavaScript files have the same name as the component's folder name. Test files h

```
checkboxes
├── checkboxes.js
├── checkboxes.mjs
└── checkboxes.test.js
```

## Skeleton

```js
import { nodeListForEach } from '../vendor/common'
import { nodeListForEach } from '../vendor/common.mjs'

function Checkboxes ($module) {
// code goes here
Expand Down Expand Up @@ -109,13 +109,15 @@ var myCheckbox = new Checkbox().init()
Use ES6 modules (`import`/`export`) over a non-standard module system. You can always transpile to your preferred module system.

```js
import { nodeListForEach } from '../vendor/common'
import { nodeListForEach } from '../vendor/common.mjs'
// code goes here
export default Checkboxes
```

Avoid using wildcard (`import * as nodeListForEach`) imports.

You must specify the file extension for a file when importing it.

Use default export over named export.

## Polyfilling
Expand All @@ -125,7 +127,7 @@ If you need to support older browsers, import the necessary [polyfills](/src/gov
For example, if you want to polyfill `addEventListener` for IE8, import the Event polyfills.

```js
import '../vendor/polyfills/Event'
import '../vendor/polyfills/Event.mjs'
```

If you need polyfills for features that are not yet included in this project, please see the following guide on [how to add polyfills](../polyfilling.md).
Expand Down
2 changes: 1 addition & 1 deletion docs/contributing/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ This task will:
**`gulp watch`**

This task will:
- watch for changes in .js, .scss and .njk files and run below tasks.
- watch for changes in .mjs, .scss and .njk files and run below tasks.

**`gulp styles`**

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
"/dist/**/*.js",
"/package/**/*.js",
"/package/**/*.mjs",
"/src/govuk/vendor/polyfills/**/*.js"
"/src/govuk/vendor/polyfills/**/*.mjs"
]
},
"jest": {
Expand Down
24 changes: 12 additions & 12 deletions src/govuk/all.js → src/govuk/all.mjs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { nodeListForEach } from './common'
import Accordion from './components/accordion/accordion'
import Button from './components/button/button'
import Details from './components/details/details'
import CharacterCount from './components/character-count/character-count'
import Checkboxes from './components/checkboxes/checkboxes'
import ErrorSummary from './components/error-summary/error-summary'
import NotificationBanner from './components/notification-banner/notification-banner'
import Header from './components/header/header'
import Radios from './components/radios/radios'
import SkipLink from './components/skip-link/skip-link'
import Tabs from './components/tabs/tabs'
import { nodeListForEach } from './common.mjs'
import Accordion from './components/accordion/accordion.mjs'
import Button from './components/button/button.mjs'
import Details from './components/details/details.mjs'
import CharacterCount from './components/character-count/character-count.mjs'
import Checkboxes from './components/checkboxes/checkboxes.mjs'
import ErrorSummary from './components/error-summary/error-summary.mjs'
import NotificationBanner from './components/notification-banner/notification-banner.mjs'
import Header from './components/header/header.mjs'
import Radios from './components/radios/radios.mjs'
import SkipLink from './components/skip-link/skip-link.mjs'
import Tabs from './components/tabs/tabs.mjs'

function initAll (options) {
// Set the options to an empty object by default if no options are passed.
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@

*/

import { nodeListForEach } from '../../common'
import '../../vendor/polyfills/Function/prototype/bind'
import '../../vendor/polyfills/Element/prototype/classList'
import { nodeListForEach } from '../../common.mjs'
import '../../vendor/polyfills/Function/prototype/bind.mjs'
import '../../vendor/polyfills/Element/prototype/classList.mjs'

function Accordion ($module) {
this.$module = $module
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import '../../vendor/polyfills/Event' // addEventListener and event.target normaliziation
import '../../vendor/polyfills/Function/prototype/bind'
import '../../vendor/polyfills/Event.mjs' // addEventListener and event.target normaliziation
import '../../vendor/polyfills/Function/prototype/bind.mjs'

var KEY_SPACE = 32
var DEBOUNCE_TIMEOUT_IN_SECONDS = 1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import '../../vendor/polyfills/Function/prototype/bind'
import '../../vendor/polyfills/Event' // addEventListener and event.target normaliziation
import '../../vendor/polyfills/Element/prototype/classList'
import '../../vendor/polyfills/Function/prototype/bind.mjs'
import '../../vendor/polyfills/Event.mjs' // addEventListener and event.target normaliziation
import '../../vendor/polyfills/Element/prototype/classList.mjs'

function CharacterCount ($module) {
this.$module = $module
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import '../../vendor/polyfills/Function/prototype/bind'
import '../../vendor/polyfills/Function/prototype/bind.mjs'
// addEventListener, event.target normalization and DOMContentLoaded
import '../../vendor/polyfills/Event'
import '../../vendor/polyfills/Element/prototype/classList'
import { nodeListForEach } from '../../common'
import '../../vendor/polyfills/Event.mjs'
import '../../vendor/polyfills/Element/prototype/classList.mjs'
import { nodeListForEach } from '../../common.mjs'

function Checkboxes ($module) {
this.$module = $module
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
*
* http://caniuse.com/#feat=details
*/
import '../../vendor/polyfills/Function/prototype/bind'
import '../../vendor/polyfills/Event' // addEventListener and event.target normaliziation
import { generateUniqueID } from '../../common'
import '../../vendor/polyfills/Function/prototype/bind.mjs'
import '../../vendor/polyfills/Event.mjs' // addEventListener and event.target normaliziation
import { generateUniqueID } from '../../common.mjs'

var KEY_ENTER = 13
var KEY_SPACE = 32
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import '../../vendor/polyfills/Function/prototype/bind'
import '../../vendor/polyfills/Event' // addEventListener
import '../../vendor/polyfills/Element/prototype/closest'
import '../../vendor/polyfills/Function/prototype/bind.mjs'
import '../../vendor/polyfills/Event.mjs' // addEventListener
import '../../vendor/polyfills/Element/prototype/closest.mjs'

function ErrorSummary ($module) {
this.$module = $module
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import '../../vendor/polyfills/Event'
import '../../vendor/polyfills/Element/prototype/classList'
import '../../vendor/polyfills/Function/prototype/bind'
import '../../vendor/polyfills/Event.mjs'
import '../../vendor/polyfills/Element/prototype/classList.mjs'
import '../../vendor/polyfills/Function/prototype/bind.mjs'

function Header ($module) {
this.$module = $module
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import '../../vendor/polyfills/Event' // addEventListener
import '../../vendor/polyfills/Event.mjs' // addEventListener

function NotificationBanner ($module) {
this.$module = $module
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import '../../vendor/polyfills/Function/prototype/bind'
import '../../vendor/polyfills/Function/prototype/bind.mjs'
// addEventListener, event.target normalization and DOMContentLoaded
import '../../vendor/polyfills/Event'
import '../../vendor/polyfills/Element/prototype/classList'
import { nodeListForEach } from '../../common'
import '../../vendor/polyfills/Event.mjs'
import '../../vendor/polyfills/Element/prototype/classList.mjs'
import { nodeListForEach } from '../../common.mjs'

function Radios ($module) {
this.$module = $module
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import '../../vendor/polyfills/Function/prototype/bind'
import '../../vendor/polyfills/Element/prototype/classList'
import '../../vendor/polyfills/Event' // addEventListener and event.target normalization
import '../../vendor/polyfills/Function/prototype/bind.mjs'
import '../../vendor/polyfills/Element/prototype/classList.mjs'
import '../../vendor/polyfills/Event.mjs' // addEventListener and event.target normalization

function SkipLink ($module) {
this.$module = $module
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import '../../vendor/polyfills/Function/prototype/bind'
import '../../vendor/polyfills/Element/prototype/classList'
import '../../vendor/polyfills/Element/prototype/nextElementSibling'
import '../../vendor/polyfills/Element/prototype/previousElementSibling'
import '../../vendor/polyfills/Event' // addEventListener and event.target normaliziation
import { nodeListForEach } from '../../common'
import '../../vendor/polyfills/Function/prototype/bind.mjs'
import '../../vendor/polyfills/Element/prototype/classList.mjs'
import '../../vendor/polyfills/Element/prototype/nextElementSibling.mjs'
import '../../vendor/polyfills/Element/prototype/previousElementSibling.mjs'
import '../../vendor/polyfills/Event.mjs' // addEventListener and event.target normaliziation
import { nodeListForEach } from '../../common.mjs'

function Tabs ($module) {
this.$module = $module
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import './Document'
import './Document.mjs'

(function(undefined) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import '../../Object/defineProperty'
import '../../DOMTokenList'
import '../../Element'
import '../../Object/defineProperty.mjs'
import '../../DOMTokenList.mjs'
import '../../Element.mjs'

(function(undefined) {

Expand Down Expand Up @@ -90,4 +90,4 @@ import '../../Element'
addProp(global.HTMLAreaElement, "relList", "rel");
}(this));

}).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
}).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import './matches'
import './matches.mjs'

(function(undefined) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import '../../Object/defineProperty'
import '../../Element'
import '../../Object/defineProperty.mjs'
import '../../Element.mjs'

(function(undefined) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import '../../Object/defineProperty'
import '../../Element'
import '../../Object/defineProperty.mjs'
import '../../Element.mjs'

(function(undefined) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import './Window'
import './Element'
import './Object/defineProperty'
import './Window.mjs'
import './Element.mjs'
import './Object/defineProperty.mjs'

(function(undefined) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import '../../Object/defineProperty'
import '../../Object/defineProperty.mjs'

(function(undefined) {
// Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Function/prototype/bind/detect.js
Expand Down
9 changes: 3 additions & 6 deletions tasks/gulp/__tests__/after-build-package.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,11 @@ describe('package/', () => {
var fileWithoutSrc = file.replace(/^src\//, '')

// Account for govuk-esm folder
if (fileWithoutSrc.split('.').pop() === 'js') {
if (fileWithoutSrc.split('.').pop() === 'mjs') {
var esmFile = fileWithoutSrc.replace('govuk/', 'govuk-esm/')
var umdFile = fileWithoutSrc.replace('.mjs', '.js')

if (!esmFile.includes('vendor/')) {
esmFile = esmFile.replace('.js', '.mjs')
}

return [fileWithoutSrc, esmFile]
return [umdFile, esmFile]
} else {
return fileWithoutSrc
}
Expand Down
5 changes: 4 additions & 1 deletion tasks/gulp/compile-assets.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ gulp.task('scss:compile', function (done) {
// --------------------------------------
gulp.task('js:compile', (done) => {
// For dist/ folder we only want compiled 'all.js'
const fileLookup = isDist ? configPaths.src + 'all.js' : configPaths.src + '**/!(*.test).js'
const fileLookup = isDist ? configPaths.src + 'all.mjs' : configPaths.src + '**/!(*.test).mjs'

// Perform a synchronous search and return an array of matching file names
const srcFiles = glob.sync(fileLookup)
Expand Down Expand Up @@ -225,6 +225,9 @@ gulp.task('js:compile', (done) => {
extname: '.min.js'
})
))
.pipe(rename({
extname: '.js'
}))
.pipe(eol())
.pipe(gulp.dest(destinationPath() + newDirectoryPath))
})
Expand Down
10 changes: 3 additions & 7 deletions tasks/gulp/copy-to-destination.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ gulp.task('copy-files', () => {
return gulp.src([
configPaths.src + '**/*',
'!**/.DS_Store',
'!**/*.test.js',
'!**/*.mjs',
'!**/*.test.*',
'!' + configPaths.src + 'README.md', // Don't override the existing README in /package
'!' + configPaths.components + '**/__snapshots__/**',
'!' + configPaths.components + '**/__snapshots__/'
Expand Down Expand Up @@ -86,12 +87,7 @@ function generateFixtures (file) {
}

gulp.task('js:copy-esm', () => {
return gulp.src([configPaths.src + '**/*.js', '!' + configPaths.src + '/**/*.test.js'])
.pipe(rename(function (path) {
if (!path.dirname.includes('vendor')) {
path.extname = '.mjs'
}
}))
return gulp.src([configPaths.src + '**/*.mjs', configPaths.src + '**/*.js', '!' + configPaths.src + '/**/*.test.js'])
.pipe(gulp.dest(taskArguments.destination + '/govuk-esm/'))
})

Expand Down
2 changes: 1 addition & 1 deletion tasks/gulp/watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ const configPaths = require('../../config/paths.json')
// ---------------------------------------
gulp.task('watch', () => Promise.all([
gulp.watch([configPaths.src + '**/**/*.scss', configPaths.app + 'assets/scss/**/*.scss', configPaths.fullPageExamples + '**/*.scss'], gulp.parallel('styles', 'sassdoc')),
gulp.watch([configPaths.src + '**/**/*.js'], gulp.series('scripts'))
gulp.watch([configPaths.src + '**/**/*.mjs'], gulp.series('scripts'))
]))