diff --git a/.gitignore b/.gitignore index fc033ad110..512dea94fd 100644 --- a/.gitignore +++ b/.gitignore @@ -8,9 +8,12 @@ package-lock.json # npm package folders /components/ /page-templates/ +/templates/ /css/ /favicons/ /fonts/ /img/ /scripts/ /scss/ + +secrets.yml diff --git a/ci/README.md b/ci/README.md new file mode 100644 index 0000000000..f286e2e13f --- /dev/null +++ b/ci/README.md @@ -0,0 +1,15 @@ +#### Login to Concourse + +``` +fly -t main login -c CONCOURSE_URL +``` + +#### Add design system Pipeline + +``` +fly -t main set-pipeline -p design-system -c concourse.yml --load-vars-from secrets.yml +``` + +``` +fly -t main unpause-pipeline --pipeline design-system +``` diff --git a/ci/common.js b/ci/common.js new file mode 100644 index 0000000000..87835e9c65 --- /dev/null +++ b/ci/common.js @@ -0,0 +1,55 @@ +import * as fs from 'fs'; +import { createFolder, copyFile } from './helpers'; + +export async function copyComponents(componentsPath, newComponentsPath) { + await createFolder(newComponentsPath); + + try { + const items = await fs.readdirSync(componentsPath).filter(path => !path.includes('.njk')); + + items.forEach(item => copyComponent(item, componentsPath, newComponentsPath)); + } catch (error) { + throw new Error(error); + } +} + +async function copyComponent(componentName, componentsPath, newComponentsPath) { + try { + const componentPath = `${componentsPath}/${componentName}`; + const newComponentPath = `${newComponentsPath}/${componentName}`; + + const items = await fs + .readdirSync(componentPath) + .filter(path => path.includes('.njk') && path.includes('_') && !path.includes('_test-')); + + if (items.length) { + await createFolder(newComponentPath); + } + + items.forEach(async path => { + await copyFile(`${componentPath}/${path}`, `${newComponentPath}/${path}`); + }); + } catch (error) { + throw new Error(error); + } +} + +export async function copyTemplates(templatesPath, newTemplatesPath) { + await createFolder(newTemplatesPath); + + try { + const items = await fs.readdirSync(templatesPath).filter(path => path.includes('.njk') && path.includes('_')); + + items.forEach(item => copyTemplate(item, templatesPath, newTemplatesPath)); + } catch (error) { + throw new Error(error); + } +} + +async function copyTemplate(templateFileName, templatesPath, newTemplatesPath) { + try { + await copyFile(`${templatesPath}/${templateFileName}`, `${newTemplatesPath}/${templateFileName}`); + } catch (error) { + throw new Error(error); + } +} diff --git a/ci/concourse.yml b/ci/concourse.yml new file mode 100644 index 0000000000..73b5f8f0ae --- /dev/null +++ b/ci/concourse.yml @@ -0,0 +1,118 @@ +resource_types: +- name: slack-notification + type: docker-image + source: + repository: cfcommunity/slack-notification-resource + tag: latest + +resources: +- name: ons-design-system-release + type: github-release + source: + owner: ONSdigital + repository: design-system + access_token: ((github_access_token)) + release: true + pre_release: true + +- name: slack-alert + type: slack-notification + source: + url: ((slack_webhook_url)) + +jobs: +- name: Release + plan: + - get: ons-design-system-release + params: + include_source_tarball: true + trigger: true + + - task: CDN Build + config: + platform: linux + + image_resource: + type: docker-image + source: + repository: node + tag: 10.14.2 + + inputs: + - name: ons-design-system-release + + outputs: + - name: dist + - name: templates + + run: + path: sh + args: + - -exc + - | + apt-get update && apt-get install -y --allow-unauthenticated zip + + cd ons-design-system-release + + mkdir design-system + tar -xzf source.tar.gz -C design-system --strip-components=1 + + cd design-system + + design_system_release=$(cat ../../ons-design-system-release/version) + + yarn + RELEASE_VERSION=$design_system_release yarn cdn-bundle + + mkdir ../../dist/$design_system_release + cp -R build/* ../../dist/$design_system_release + zip -r ../../templates/templates.zip templates/* + on_failure: + put: slack-alert + params: + channel: '#pat-lib-notifications' + attachments: + - pretext: Design System Build Failed + color: danger + title: Concourse Build $BUILD_ID + title_link: http://concourse.dev.eq.ons.digital/builds/$BUILD_ID + + - task: Release to S3 + params: + AWS_ACCESS_KEY_ID: ((aws_access_key)) + AWS_SECRET_ACCESS_KEY: ((aws_secret_key)) + AWS_DEFAULT_REGION: eu-west-1 + config: + platform: linux + + image_resource: + type: docker-image + source: + repository: mesosphere/aws-cli + + inputs: + - name: dist + + run: + path: sh + args: + - -exc + - | + aws s3 sync --acl public-read dist s3://((s3_bucket_name))/design-system/ + + on_failure: + put: slack-alert + params: + channel: '#pat-lib-notifications' + attachments: + - pretext: Design System CDN Release Failed + color: danger + title: Concourse Build $BUILD_ID + title_link: http://concourse.dev.eq.ons.digital/builds/$BUILD_ID + + - put: ons-design-system-release + params: + name: ons-design-system-release/version + tag: ons-design-system-release/version + globs: + - templates/*.zip diff --git a/ci/generate-npm-package.js b/ci/generate-npm-package.js new file mode 100644 index 0000000000..3352de86e2 --- /dev/null +++ b/ci/generate-npm-package.js @@ -0,0 +1,65 @@ +import * as fs from 'fs'; + +import { removeFolder, copyDir, createFolder, copyFile, asyncForEach } from './helpers'; +import { copyComponents, copyTemplates } from './common'; + +const cwd = process.cwd(); +const sourcePath = `${cwd}/src`; +const componentsPath = `${sourcePath}/components`; +const newComponentsPath = `${cwd}/components`; +const templatesPath = `${sourcePath}/styles/page-template`; +const newTemplatesPath = `${cwd}/page-templates`; +const assetFolders = ['css', 'favicons', 'fonts', 'img', 'scripts']; +const builtAssetsFolders = assetFolders.map(folder => `${cwd}/build/${folder}`); +const newSassPath = `${cwd}/scss`; + +async function removeExistingFolders() { + const folders = [newComponentsPath, newTemplatesPath, ...assetFolders]; + + await asyncForEach(folders, removeFolder); +} + +async function copyAssets() { + await asyncForEach(assetFolders, async (folder, index) => { + await createFolder(folder); + + try { + const builtPath = builtAssetsFolders[index]; + const files = await fs.readdirSync(builtPath).filter(path => !path.includes('patternlib')); + + asyncForEach(files, async file => { + if (file.match(/(\.\w+)$/)) { + await copyFile(`${builtPath}/${file}`, `${folder}/${file}`); + } else { + const newFolderPath = `${folder}/${file}`; + const nestedBuiltPath = `${builtPath}/${file}`; + await createFolder(newFolderPath); + + const nestedFiles = await fs.readdirSync(nestedBuiltPath).filter(path => !path.includes('patternlib')); + + asyncForEach(nestedFiles, async nestedFile => { + await copyFile(`${nestedBuiltPath}/${nestedFile}`, `${newFolderPath}/${nestedFile}`); + }); + } + }); + } catch (error) { + throw new Error(error); + } + }); +} + +async function copyBaseSass() { + await createFolder(newSassPath); + await copyDir(`${sourcePath}/scss/helpers`, `${newSassPath}/helpers`); + await copyDir(`${sourcePath}/scss/vars`, `${newSassPath}/vars`); +} + +async function run() { + await removeExistingFolders(); + await copyComponents(componentsPath, newComponentsPath); + await copyTemplates(templatesPath, newTemplatesPath); + await copyAssets(); + await copyBaseSass(); +} + +run(); diff --git a/ci/helpers.js b/ci/helpers.js new file mode 100644 index 0000000000..185073b496 --- /dev/null +++ b/ci/helpers.js @@ -0,0 +1,48 @@ +import * as fs from 'fs'; +import ncp from 'ncp'; +import rimraf from 'rimraf'; + +export async function copyDir(from, to) { + return new Promise((resolve, reject) => { + ncp(from, to, error => { + if (error) { + throw new Error(error); + reject(error); + } else { + resolve(); + } + }); + }); +} + +export async function createFolder(folderPath) { + try { + await fs.mkdirSync(folderPath); + } catch (error) { + throw new Error(error); + } +} + +export async function copyFile(filePath, newComponentPath) { + try { + await fs.copyFileSync(filePath, newComponentPath); + } catch (error) { + throw new Error(error); + } +} + +export async function asyncForEach(array, callback) { + for (let index = 0, arrayLength = array.length; index < arrayLength; index++) { + await callback(array[index], index, array); + } +} + +export async function removeFolder(folderPath) { + try { + if (await fs.existsSync(folderPath)) { + await rimraf.sync(folderPath); + } + } catch (error) { + throw new Error(error); + } +} diff --git a/ci/prepare-templates-for-zip.js b/ci/prepare-templates-for-zip.js new file mode 100644 index 0000000000..31059e36c1 --- /dev/null +++ b/ci/prepare-templates-for-zip.js @@ -0,0 +1,40 @@ +import prependFile from 'prepend-file'; +import { removeFolder, createFolder, asyncForEach } from './helpers'; +import { copyComponents, copyTemplates } from './common'; + +const cwd = process.cwd(); +const sourcePath = `${cwd}/src`; +const destPath = `${cwd}/templates`; +const componentsPath = `${sourcePath}/components`; +const newComponentsPath = `${destPath}/components`; +const templatesPath = `${sourcePath}/styles/page-template`; +const newTemplatesPath = `${destPath}/layout`; +const copiedPageTemplatePath = `${newTemplatesPath}/_template.njk`; + +async function removeExistingFolders() { + const folders = [newComponentsPath, newTemplatesPath]; + + await asyncForEach(folders, removeFolder); +} + +async function addCDNVersionToPageTemplate() { + const version = process.env.RELEASE_VERSION; + + if (version) { + const insert = `{% set release_version = '${version}' %}\n`; + + prependFile.sync(copiedPageTemplatePath, insert); + } else { + throw new Error('RELEASE_VERSION not specified'); + } +} + +async function run() { + await removeExistingFolders(); + await createFolder(destPath); + await copyComponents(componentsPath, newComponentsPath); + await copyTemplates(templatesPath, newTemplatesPath); + await addCDNVersionToPageTemplate(); +} + +run(); diff --git a/ci/secrets.yml.example b/ci/secrets.yml.example new file mode 100644 index 0000000000..0fc81f9440 --- /dev/null +++ b/ci/secrets.yml.example @@ -0,0 +1,5 @@ +aws_access_key: +aws_secret_key: +s3_bucket_name: +slack_webhook_url: +github_access_token: diff --git a/lib/generate-npm-package.js b/lib/generate-npm-package.js deleted file mode 100644 index e8c53d7ffb..0000000000 --- a/lib/generate-npm-package.js +++ /dev/null @@ -1,163 +0,0 @@ -import * as fs from 'fs'; - -import rimraf from 'rimraf'; -import ncp from 'ncp'; - -const cwd = process.cwd(); -const sourcePath = `${cwd}/src`; -const componentsPath = `${sourcePath}/components`; -const newComponentsPath = `${cwd}/components`; -const templatesPath = `${sourcePath}/styles/page-template`; -const newTemplatesPath = `${cwd}/page-templates`; -const assetFolders = ['css', 'favicons', 'fonts', 'img', 'scripts']; -const builtAssetsFolders = assetFolders.map(folder => `${cwd}/build/${folder}`); -const newSassPath = `${cwd}/scss`; - -async function removeExistingFolders() { - const folders = [newComponentsPath, newTemplatesPath, ...assetFolders]; - - await asyncForEach(folders, removeFolder); -} - -async function removeFolder(folderPath) { - try { - if (await fs.existsSync(folderPath)) { - await rimraf.sync(folderPath); - } - } catch (error) { - throw new Error(error); - } -} - -async function copyComponents() { - await createFolder(newComponentsPath); - - try { - const items = await fs.readdirSync(componentsPath).filter(path => !path.includes('.njk')); - - items.forEach(await copyComponent); - } catch (error) { - throw new Error(error); - } -} - -async function copyComponent(componentName) { - try { - const componentPath = `${componentsPath}/${componentName}`; - const newComponentPath = `${newComponentsPath}/${componentName}`; - - const items = await fs - .readdirSync(componentPath) - .filter(path => path.includes('.njk') && path.includes('_') && !path.includes('_test-')); - - if (items.length) { - await createFolder(newComponentPath); - } - - items.forEach(async path => { - await copyFile(`${componentPath}/${path}`, `${newComponentPath}/${path}`); - }); - } catch (error) { - throw new Error(error); - } -} - -async function copyTemplates() { - await createFolder(newTemplatesPath); - - try { - const items = await fs.readdirSync(templatesPath).filter(path => path.includes('.njk')); - - items.forEach(await copyTemplate); - } catch (error) { - throw new Error(error); - } -} - -async function copyTemplate(templateFileName) { - try { - await copyFile(`${templatesPath}/${templateFileName}`, `${newTemplatesPath}/${templateFileName}`); - } catch (error) { - throw new Error(error); - } -} - -async function copyAssets() { - await asyncForEach(assetFolders, async (folder, index) => { - await createFolder(folder); - - try { - const builtPath = builtAssetsFolders[index]; - const files = await fs.readdirSync(builtPath).filter(path => !path.includes('patternlib')); - - asyncForEach(files, async file => { - if (file.match(/(\.\w+)$/)) { - await copyFile(`${builtPath}/${file}`, `${folder}/${file}`); - } else { - const newFolderPath = `${folder}/${file}`; - const nestedBuiltPath = `${builtPath}/${file}`; - await createFolder(newFolderPath); - - const nestedFiles = await fs.readdirSync(nestedBuiltPath).filter(path => !path.includes('patternlib')); - - asyncForEach(nestedFiles, async nestedFile => { - await copyFile(`${nestedBuiltPath}/${nestedFile}`, `${newFolderPath}/${nestedFile}`); - }); - } - }); - } catch (error) { - throw new Error(error); - } - }); -} - -async function copyBaseSass() { - await createFolder(newSassPath); - await copyDir(`${sourcePath}/scss/helpers`, `${newSassPath}/helpers`); - await copyDir(`${sourcePath}/scss/vars`, `${newSassPath}/vars`); -} - -async function copyDir(from, to) { - return new Promise((resolve, reject) => { - ncp(from, to, error => { - if (error) { - throw new Error(error); - reject(error); - } else { - resolve(); - } - }); - }); -} - -async function createFolder(folderPath) { - try { - await fs.mkdirSync(folderPath); - } catch (error) { - throw new Error(error); - } -} - -async function copyFile(filePath, newComponentPath) { - try { - await fs.copyFileSync(filePath, newComponentPath); - } catch (error) { - throw new Error(error); - } -} - -async function asyncForEach(array, callback) { - for (let index = 0, arrayLength = array.length; index < arrayLength; index++) { - await callback(array[index], index, array); - } -} - -async function run() { - await removeExistingFolders(); - await copyComponents(); - await copyTemplates(); - await copyAssets(); - await copyBaseSass(); -} - -run(); diff --git a/package.json b/package.json index e5fbf38c96..49701419e3 100644 --- a/package.json +++ b/package.json @@ -10,16 +10,17 @@ "url": "https://github.com/bameyrick" }, "scripts": { - "build": "yarn tidy-clean && yarn webpack --config webpack.prod.babel.js", - "check-unused": "npx npm-check-unused", - "dedupe-deps": "npx yarn-deduplicate yarn.lock", - "lint-staged": "lint-staged", - "npm-bundle": "NODE_ENV=production yarn build && babel-node lib/generate-npm-package.js", "start": "yarn tidy-clean && yarn webpack-dev-server --host 0.0.0.0 --watch --config webpack.dev.babel.js", + "build": "yarn tidy-clean && yarn webpack --config webpack.prod.babel.js", + "npm-bundle": "NODE_ENV=production yarn tidy-clean && yarn webpack --config webpack.npm.babel.js && babel-node ci/generate-npm-package.js", + "cdn-bundle": "NODE_ENV=production yarn tidy-clean && yarn webpack --config webpack.cdn.babel.js && babel-node ci/prepare-templates-for-zip.js", + "test:local": "KARMA_SINGLE_RUN=false karma start ./src/tests/config/karma.conf.js", "test": "karma start ./src/tests/config/karma.conf.js && karma start ./src/tests/config/karma.conf.nomodule.js && codecov", "test:browserstack": "TEST_ON_BROWSERSTACK=true karma start ./src/tests/config/karma.conf.js && TEST_ON_BROWSERSTACK=true karma start ./src/tests/config/karma.conf.nomodule.js", - "test:local": "KARMA_SINGLE_RUN=false karma start ./src/tests/config/karma.conf.js", - "tidy-clean": "rm -rf build css favicons fonts img templates scripts coverage scss" + "tidy-clean": "rm -rf build css favicons fonts img components page-templates templates scripts coverage scss", + "check-unused": "npx npm-check-unused", + "dedupe-deps": "npx yarn-deduplicate yarn.lock", + "lint-staged": "lint-staged" }, "husky": { "hooks": { @@ -109,6 +110,7 @@ "postcss-loader": "^3.0.0", "postcss-mq-optimize": "^0.0.0", "postcss-url": "^8.0.0", + "prepend-file": "^1.3.1", "prettier": "^1.15.2", "pretty": "^2.0.0", "print-error": "^0.1.17", diff --git a/src/get-started/index.njk b/src/get-started/index.njk index 11da7b52b5..d19d097d33 100644 --- a/src/get-started/index.njk +++ b/src/get-started/index.njk @@ -4,22 +4,22 @@ sortOrder: 1 --- You can start using the latest version of the ONS Design System in your service right now. All of the styles, assets and components are production ready. -## Include the compiled css and js -All of the global styles and individual component code is compiled and made available via a `cdn`. +## Include the compiled CSS and JS +All of the global styles and individual component code is compiled and made available via a `CDN`. Each release of the Design System is tagged with a version e.g. ![](https://img.shields.io/github/release/onsdigital/design-system.svg?style=flat-square) which forms part of the `url` of the compiled files for a specific release. -The following files are deployed to the `cdn` which can be referenced in a service or project: +The following files are deployed to the `CDN` which can be referenced in a service or project: -- `main.css` - Responsive css file of all components. -- `bundle.min.js` - Bundled javascript modules. +- `main.css` - Responsive CSS file of all components. +- `main.js` - Bundled JavaScript modules. To reference the files in your service you would use the following `url` structures (the `[VERSION]` should be replaced with the required release version e.g. ![](https://img.shields.io/github/release/onsdigital/design-system.svg?style=flat-square)): -- `https://cdn.ons.gov.uk/sdc/[VERSION]/css/main.css` -- `https://cdn.ons.gov.uk/sdc/[VERSION]/js/bundle.min.js` +- `https://cdn.ons.gov.uk/design-system/[VERSION]/css/main.css` +- `https://cdn.ons.gov.uk/design-system/[VERSION]/js/main.js` -Each release deployed to the `cdn` is always available. This allows services to plan upgrades to new versions. +Each release deployed to the `CDN` is always available. This allows services to plan upgrades to new versions. ## Favicons @@ -27,7 +27,7 @@ Favicons have been created for Safari, Chrome and Edge/Internet Explorer for des - <meta name="msapplication-config" content="https://cdn.ons.gov.uk/sdc/[VERSION]/favicons/browserconfig.json"> + <meta name="msapplication-config" content="https://cdn.ons.gov.uk/design-system/[VERSION]/favicons/browserconfig.json">
@@ -35,23 +35,23 @@ Favicons have been created for Safari, Chrome and Edge/Internet Explorer for des
- <link rel="icon" type="image/png" href="https://cdn.ons.gov.uk/sdc/[VERSION]/favicons/favicon-32x32.png" sizes="32x32"> + <link rel="icon" type="image/png" href="https://cdn.ons.gov.uk/design-system/[VERSION]/favicons/favicon-32x32.png" sizes="32x32">
- <link rel="icon" type="image/png" href="https://cdn.ons.gov.uk/sdc/[VERSION]/favicons/favicon-16x16.png" sizes="16x16"> + <link rel="icon" type="image/png" href="https://cdn.ons.gov.uk/design-system/[VERSION]/favicons/favicon-16x16.png" sizes="16x16">
- <link rel="mask-icon" color="#5bbad5" href="https://cdn.ons.gov.uk/sdc/[VERSION]/favicons/safari-pinned-tab.svg"> + <link rel="mask-icon" color="#5bbad5" href="https://cdn.ons.gov.uk/design-system/[VERSION]/favicons/safari-pinned-tab.svg">
- <link rel="apple-touch-icon" type="image/png" href="https://cdn.ons.gov.uk/sdc/[VERSION]/favicons/apple-touch-icon.png" sizes="180x180"> + <link rel="apple-touch-icon" type="image/png" href="https://cdn.ons.gov.uk/design-system/[VERSION]/favicons/apple-touch-icon.png" sizes="180x180">
- <link rel="manifest" href="https://cdn.ons.gov.uk/sdc/[VERSION]/favicons/manifest.json"> + <link rel="manifest" href="https://cdn.ons.gov.uk/design-system/[VERSION]/favicons/manifest.json">
diff --git a/src/img/check-green.png b/src/img/check-green.png deleted file mode 100644 index f1edf9e950..0000000000 Binary files a/src/img/check-green.png and /dev/null differ diff --git a/src/img/favicon_chrome_tab.png b/src/img/favicon_chrome_tab.png deleted file mode 100644 index a400ffe888..0000000000 Binary files a/src/img/favicon_chrome_tab.png and /dev/null differ diff --git a/src/img/favicon_safari_homepage.png b/src/img/favicon_safari_homepage.png deleted file mode 100644 index 958a1521a7..0000000000 Binary files a/src/img/favicon_safari_homepage.png and /dev/null differ diff --git a/src/img/favicon_series_winphone_nokialumia930.png b/src/img/favicon_series_winphone_nokialumia930.png deleted file mode 100644 index 7cc75da96b..0000000000 Binary files a/src/img/favicon_series_winphone_nokialumia930.png and /dev/null differ diff --git a/src/img/favicons_winphone_nokialumia930.png b/src/img/favicons_winphone_nokialumia930.png deleted file mode 100644 index b4cb024f25..0000000000 Binary files a/src/img/favicons_winphone_nokialumia930.png and /dev/null differ diff --git a/src/styles/page-template/_template.njk b/src/styles/page-template/_template.njk index 398de63346..734e574c21 100644 --- a/src/styles/page-template/_template.njk +++ b/src/styles/page-template/_template.njk @@ -9,9 +9,9 @@ {% set currentLanguageISOCode = currentLanguage.ISOCode %} {% endif %} -{% if page.cdn %} +{% if page.cdn or release_version %} {# Production #} - {% set assetsURL = "https://cdn.ons.gov.uk/sdc/" + page.cdn %} + {% set assetsURL = "https://cdn.ons.gov.uk/design-system/" + (page.cdn or release_version) %} {% elif data and data.version %} {# Prototype kits #} {% set assetsURL = "/" + data.version %} @@ -129,9 +129,9 @@ {% endif %} {% if isPatternLib %} - {% set scripts = assetsURL + "/scripts/bundle.js," + assetsURL + "/scripts/patternlib.js" %} + {% set scripts = assetsURL + "/scripts/main.js," + assetsURL + "/scripts/patternlib.js" %} {% else %} - {% set scripts = assetsURL + "/scripts/bundle.js" %} + {% set scripts = assetsURL + "/scripts/main.js" %} {% endif %} {% include './_bundle-loader.njk' %} diff --git a/src/views/layouts/_master.njk b/src/views/layouts/_master.njk index 0e985c9b9d..3067b5d5c6 100644 --- a/src/views/layouts/_master.njk +++ b/src/views/layouts/_master.njk @@ -13,7 +13,7 @@ {% block master_body %} {% endblock %} - {% set scripts = "/scripts/bundle.js,/scripts/patternlib.js" %} + {% set scripts = "/scripts/main.js,/scripts/patternlib.js" %} {% include 'styles/page-template/_bundle-loader.njk' %} {% if devMode %} diff --git a/src/views/layouts/_prototype.njk b/src/views/layouts/_prototype.njk index 14a54ac0a4..b165972326 100644 --- a/src/views/layouts/_prototype.njk +++ b/src/views/layouts/_prototype.njk @@ -13,7 +13,7 @@ {% block prototype_body %} {% endblock %} - {% set scripts = "/scripts/bundle.js,/scripts/patternlib.js" %} + {% set scripts = "/scripts/main.js,/scripts/patternlib.js" %} {% include 'styles/page-template/_bundle-loader.njk' %} {% if devMode %} diff --git a/webpack.cdn.babel.js b/webpack.cdn.babel.js new file mode 100644 index 0000000000..f25c1ea748 --- /dev/null +++ b/webpack.cdn.babel.js @@ -0,0 +1,6 @@ +import merge from 'webpack-merge'; +import commonConfig from './webpack.common'; + +const common = commonConfig('production'); + +export default [merge(common.assets, {}), merge(common.es2015plus, {}), merge(common.es5, {})]; diff --git a/webpack.common.js b/webpack.common.js index 5df558d3fc..df245713f3 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -51,12 +51,50 @@ const core = { ] }; -const jsCore = merge(core, { - entry: { - 'scripts/bundle': ['./js/public-path-override.js', './js/polyfills/index.js', './js/index.js'], - 'scripts/patternlib': ['./js/patternlib/index.js'] +const cssCore = merge(core, { + module: { + rules: [ + // Styles + { + include: [path.join(process.cwd(), 'src/scss')], + test: /\.scss$/, + use: [ + { + loader: 'file-loader', + options: { + name: 'css/[name].css' + } + }, + { + loader: 'postcss-loader', + options: { + indent: 'postcss', + plugins: postcssPlugins + } + }, + { + loader: 'sass-loader', + options: { + sourceMap: false, + precision: 8, + includePaths: [path.join(process.cwd(), 'src/scss')], + importer: globImporter() + } + } + ] + } + ] }, + plugins: [ + new FixStyleOnlyEntriesPlugin({ + extensions: ['scss', 'njk', 'html'], + silent: true + }) + ] +}); + +const jsCore = merge(core, { output: { chunkFilename: 'scripts/[name].js', publicPath: '/' @@ -92,84 +130,100 @@ const jsCore = merge(core, { // } }); +const es2015plusCore = merge(jsCore, { + module: { + rules: [ + { + test: /\.js$/, + exclude: /(node_modules)/, + use: { + loader: 'babel-loader', + options: { + babelrc: false, + presets: [ + [ + '@babel/preset-env', + { + modules: false, + targets: { + browsers: ['Chrome >= 60', 'Safari >= 10.1', 'iOS >= 10.3', 'Firefox >= 54', 'Edge >= 15'] + } + } + ] + ], + plugins: [ + '@babel/plugin-syntax-dynamic-import', + '@babel/plugin-proposal-class-properties', + '@babel/plugin-transform-runtime', + 'rewiremock/babel' + ] + } + } + } + ] + } +}); + +const es5Core = merge(jsCore, { + output: { + filename: '[name].es5.js', + chunkFilename: 'scripts/[name].es5.js' + }, + + module: { + rules: [ + { + test: /\.js$/, + exclude: /(node_modules)/, + use: { + loader: 'babel-loader', + options: { + babelrc: false, + presets: [ + [ + '@babel/preset-env', + { + modules: false, + targets: { + browsers: ['last 3 versions'] + } + } + ] + ], + plugins: [ + '@babel/plugin-syntax-dynamic-import', + '@babel/plugin-proposal-class-properties', + '@babel/plugin-transform-runtime', + 'rewiremock/babel' + ] + } + } + } + ] + } +}); + +const scriptsEntry = { + 'scripts/main': ['./js/public-path-override.js', './js/polyfills/index.js', './js/index.js'] +}; + +const patternLibScriptsEntry = { + 'scripts/patternlib': ['./js/patternlib/index.js'] +}; + export default function(mode) { const devMode = mode === 'development'; return { - nonJs: merge(core, { + assets: merge(cssCore, { mode, entry: { 'css/main': ['./scss/main.scss'], - 'css/census': ['./scss/census.scss'], - 'css/patternlib': ['./scss/patternlib.scss'], - error: ['./scss/error.scss'], - html: glob.sync('./**/*.{njk,html}', { cwd: 'src', ignore: './**/_*.{njk,html}' }) - }, - - module: { - rules: [ - // Styles - { - include: [path.join(process.cwd(), 'src/scss')], - test: /\.scss$/, - use: [ - { - loader: 'file-loader', - options: { - name: 'css/[name].css' - } - }, - { - loader: 'postcss-loader', - options: { - indent: 'postcss', - plugins: postcssPlugins - } - }, - { - loader: 'sass-loader', - options: { - sourceMap: false, - precision: 8, - includePaths: [path.join(process.cwd(), 'src/scss')], - importer: globImporter() - } - } - ] - }, - // Templates - { - test: /\.(njk|html)$/, - loaders: [ - { - loader: 'file-loader', - options: { - name: '[path][name].html' - } - }, - { - loader: path.resolve('./lib/nunjucks-html-loader.js'), - options: { - searchPaths: `${__dirname}/src`, - layoutPath: 'views/layouts', - context: { - devMode, - isPatternLib: true - } - } - } - ] - } - ] + 'css/census': ['./scss/census.scss'] }, plugins: [ - new FixStyleOnlyEntriesPlugin({ - extensions: ['scss', 'njk', 'html'], - silent: true - }), - new CopyWebpackPlugin( [ { @@ -184,12 +238,6 @@ export default function(mode) { dot: true } }, - { - from: { - glob: 'patternlib-img/**/*', - dot: true - } - }, { from: { glob: 'favicons/**/*', @@ -212,81 +260,103 @@ export default function(mode) { ] }), - es2015plus: merge(jsCore, { + patternLibAssets: merge(cssCore, { mode, - module: { - rules: [ - { - test: /\.js$/, - exclude: /(node_modules)/, - use: { - loader: 'babel-loader', - options: { - babelrc: false, - presets: [ - [ - '@babel/preset-env', - { - modules: false, - targets: { - browsers: ['Chrome >= 60', 'Safari >= 10.1', 'iOS >= 10.3', 'Firefox >= 54', 'Edge >= 15'] - } - } - ] - ], - plugins: [ - '@babel/plugin-syntax-dynamic-import', - '@babel/plugin-proposal-class-properties', - '@babel/plugin-transform-runtime', - 'rewiremock/babel' - ] + entry: { + 'css/patternlib': ['./scss/patternlib.scss'], + error: ['./scss/error.scss'] + }, + + plugins: [ + new CopyWebpackPlugin( + [ + { + from: { + glob: 'patternlib-img/**/*', + dot: true } } + ], + { + ignore: ['.gitkeep'], + debug: 'warning' } - ] - } + ), + + new ImageminPlugin({ + test: /\.(svg)$/i, + svgo: { + plugins: svgoConfig + } + }) + ] }), - es5: merge(jsCore, { + templates: merge(core, { mode, - output: { - filename: '[name].es5.js', - chunkFilename: 'scripts/[name].es5.js' + entry: { + html: glob.sync('./**/*.{njk,html}', { cwd: 'src', ignore: './**/_*.{njk,html}' }) }, module: { rules: [ { - test: /\.js$/, - exclude: /(node_modules)/, - use: { - loader: 'babel-loader', - options: { - babelrc: false, - presets: [ - [ - '@babel/preset-env', - { - modules: false, - targets: { - browsers: ['last 3 versions'] - } - } - ] - ], - plugins: [ - '@babel/plugin-syntax-dynamic-import', - '@babel/plugin-proposal-class-properties', - '@babel/plugin-transform-runtime', - 'rewiremock/babel' - ] + test: /\.(njk|html)$/, + loaders: [ + { + loader: 'file-loader', + options: { + name: '[path][name].html' + } + }, + { + loader: path.resolve('./lib/nunjucks-html-loader.js'), + options: { + searchPaths: `${__dirname}/src`, + layoutPath: 'views/layouts', + context: { + devMode, + isPatternLib: true + } + } } - } + ] } ] - } + }, + + plugins: [ + new FixStyleOnlyEntriesPlugin({ + extensions: ['scss', 'njk', 'html'], + silent: true + }) + ] + }), + + es2015plus: merge(es2015plusCore, { + mode, + + entry: scriptsEntry + }), + + es2015plusPatternLib: merge(es2015plusCore, { + mode, + + entry: patternLibScriptsEntry + }), + + es5: merge(es5Core, { + mode, + + entry: scriptsEntry + }), + + es5PatternLib: merge(es5Core, { + mode, + + entry: patternLibScriptsEntry }) }; } diff --git a/webpack.dev.babel.js b/webpack.dev.babel.js index 3600621c35..eeb0dd401c 100644 --- a/webpack.dev.babel.js +++ b/webpack.dev.babel.js @@ -47,4 +47,12 @@ const serverSettings = { plugins: [new LiveReloadPlugin()] }; -export default [merge(common.nonJs, serverSettings), merge(common.es2015plus, serverSettings), merge(common.es5, serverSettings)]; +export default [ + merge(common.assets, serverSettings), + merge(common.patternLibAssets, serverSettings), + merge(common.templates, serverSettings), + merge(common.es2015plus, serverSettings), + merge(common.es5, serverSettings), + merge(common.es2015plusPatternLib, serverSettings), + merge(common.es5PatternLib, serverSettings) +]; diff --git a/webpack.npm.babel.js b/webpack.npm.babel.js new file mode 100644 index 0000000000..f25c1ea748 --- /dev/null +++ b/webpack.npm.babel.js @@ -0,0 +1,6 @@ +import merge from 'webpack-merge'; +import commonConfig from './webpack.common'; + +const common = commonConfig('production'); + +export default [merge(common.assets, {}), merge(common.es2015plus, {}), merge(common.es5, {})]; diff --git a/webpack.prod.babel.js b/webpack.prod.babel.js index 707ad1d071..8e232c04af 100644 --- a/webpack.prod.babel.js +++ b/webpack.prod.babel.js @@ -6,7 +6,9 @@ import commonConfig from './webpack.common'; const common = commonConfig('production'); export default [ - merge(common.nonJs, {}), + merge(common.assets, {}), + merge(common.patternLibAssets, {}), + merge(common.templates, {}), merge(common.es2015plus, { plugins: [ new BundleAnalyzerPlugin({ @@ -32,5 +34,7 @@ export default [ excludeAssets: /patternlib.es5.js/ }) ] - }) + }), + merge(common.es2015plusPatternLib, {}), + merge(common.es5PatternLib, {}) ]; diff --git a/yarn.lock b/yarn.lock index d17c1adcdb..e93024d1b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7435,7 +7435,7 @@ os-locale@^3.0.0: lcid "^2.0.0" mem "^4.0.0" -os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: +os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= @@ -8202,6 +8202,13 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prepend-file@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/prepend-file/-/prepend-file-1.3.1.tgz#83b16e0b4ac1901fce88dbd945a22f4cc81df579" + integrity sha1-g7FuC0rBkB/OiNvZRaIvTMgd9Xk= + dependencies: + tmp "0.0.31" + prepend-http@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" @@ -10171,6 +10178,13 @@ title-case@^2.1.0: no-case "^2.2.0" upper-case "^1.0.3" +tmp@0.0.31: + version "0.0.31" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" + integrity sha1-jzirlDjhcxXl29izZX6L+yd65Kc= + dependencies: + os-tmpdir "~1.0.1" + tmp@0.0.33, tmp@0.0.x, tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"