Skip to content

Commit

Permalink
Merge pull request #42 from emulsify-ds/develop
Browse files Browse the repository at this point in the history
Release: SDC support, Storybook8, flexible Webpack compiling, and dependency updates
  • Loading branch information
callinmullaney authored Sep 3, 2024
2 parents 8a28c7c + 03f851e commit c61f703
Show file tree
Hide file tree
Showing 16 changed files with 10,187 additions and 14,352 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/addtoprojects.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ jobs:
# You can target a repository in a different organization
# to the issue
project-url: https://github.com/orgs/emulsify-ds/projects/6
token: ${{ secrets.ADD_TO_PROJECT_PAT }}
github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}
7 changes: 6 additions & 1 deletion .storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ const safeConfigOverrides = configOverrides || {};

const config = {
stories: [
'../../../../components/**/*.stories.@(js|jsx|ts|tsx)',
'../../../../(src|components)/**/*.stories.@(js|jsx|ts|tsx)',
],
staticDirs: [
'../../../../assets/images',
'../../../../assets/icons',
'../../../../dist',
],
addons: [
'../../../@storybook/addon-a11y',
Expand Down
3 changes: 1 addition & 2 deletions .storybook/manager.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { addons } from '@storybook/addons';

import { addons } from '@storybook/manager-api';
import emulsifyTheme from './emulsifyTheme';

import('../../../../config/emulsify-core/storybook/theme')
Expand Down
7 changes: 2 additions & 5 deletions .storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { useEffect } from '@storybook/client-api';
import { useEffect } from '@storybook/preview-api';
import Twig from 'twig';
import { setupTwig } from './setupTwig';

// GLOBAL CSS
import('../../../../dist/css/style.css');

// Custom theme preview config if it exists.
// Project config to import stylesheets.
import('../../../../config/emulsify-core/storybook/preview');

// If in a Drupal project, it's recommended to import a symlinked version of drupal.js.
Expand Down
76 changes: 64 additions & 12 deletions .storybook/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,66 @@
const path = require('path');
const globImporter = require('node-sass-glob-importer');

const _StyleLintPlugin = require('stylelint-webpack-plugin');
const { namespaces } = require('./setupTwig');
const ESLintPlugin = require('eslint-webpack-plugin');
const resolves = require('../config/webpack/resolves');

// Emulsify project configuration.
const emulsifyConfig = require('../../../../project.emulsify.json');

/**
* Transforms namespace:component to @namespace/template/path
*/
class ProjectNameResolverPlugin {
constructor(options = {}) {
this.prefix = options.projectName;
}

apply(resolver) {
const target = resolver.ensureHook('resolve');
resolver
.getHook('before-resolve')
.tapAsync('ProjectNameResolverPlugin', (request, resolveContext, callback) => {
const requestPath = request.request;

if (requestPath && requestPath.startsWith(`${this.prefix}:`)) {
const newRequestPath = requestPath.replace(`${this.prefix}:`, `${this.prefix}/`);
const newRequest = {
...request,
request: newRequestPath,
};

resolver.doResolve(
target,
newRequest,
`Resolved ${this.prefix} URI: ${resolves.TwigResolve.alias[requestPath]}`,
resolveContext,
callback
);
} else {
callback();
}
});
}
}

module.exports = async ({ config }) => {
// Alias
Object.assign(config.resolve.alias, resolves.TwigResolve.alias);

// Twig
config.module.rules.push({
test: /\.twig$/,
use: [
{
loader: 'twig-loader',
loader: path.resolve(__dirname, '../config/webpack/sdc-loader.js'),
options: {
twigOptions: {
namespaces,
},
projectName: emulsifyConfig.project.name,
},
},
{
loader: 'twigjs-loader',
},
],
});

Expand All @@ -43,25 +87,33 @@ module.exports = async ({ config }) => {
],
});

// YAML
config.module.rules.push({
test: /\.ya?ml$/,
loader: 'js-yaml-loader',
});

// Plugins
config.plugins.push(
new _StyleLintPlugin({
configFile: path.resolve(__dirname, '../', '.stylelintrc.json'),
context: path.resolve(__dirname, '../', 'components'),
context: path.resolve(__dirname, '../', 'src'),
files: '**/*.scss',
failOnError: false,
quiet: false,
}),
new ESLintPlugin({
context: path.resolve(__dirname, '../', 'components'),
context: path.resolve(__dirname, '../', 'src'),
extensions: ['js'],
}),
);

// YAML
config.module.rules.push({
test: /\.ya?ml$/,
loader: 'js-yaml-loader',
});
// Resolver Plugins
config.resolve.plugins = [
new ProjectNameResolverPlugin({
projectName: emulsifyConfig.project.name,
}),
];

return config;
};
1 change: 0 additions & 1 deletion config/eslintrc.config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"extends": [
"airbnb-base",
"eslint:recommended",
"plugin:import/recommended",
"plugin:security/recommended-legacy",
Expand Down
1 change: 0 additions & 1 deletion config/webpack/css.js

This file was deleted.

1 change: 0 additions & 1 deletion config/webpack/css/style.js

This file was deleted.

14 changes: 11 additions & 3 deletions config/webpack/loaders.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ const JSLoader = {
};

const ImageLoader = {
test: /\.(png|svg|jpg|gif)$/i,
exclude: /icons\/.*\.svg$/,
loader: 'file-loader',
test: /\.(png|jpe?g|gif)$/i,
type: 'asset',
};

const CSSLoader = {
Expand Down Expand Up @@ -76,13 +75,22 @@ const SVGSpriteLoader = {
options: {
extract: true,
runtimeCompat: true,
outputPath: 'dist/',
spriteFilename: './icons.svg',
},
};

const TwigLoader = {
test: /\.twig$/,
use: {
loader: 'twigjs-loader',
},
};

module.exports = {
JSLoader,
CSSLoader,
SVGSpriteLoader,
ImageLoader,
TwigLoader,
};
18 changes: 18 additions & 0 deletions config/webpack/optimizers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');

const ImageMinimizer = new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminMinify,
options: {
plugins: [
['gifsicle', { interlaced: true }],
['jpegtran', { progressive: true }],
['optipng', { optimizationLevel: 5 }],
],
},
},
});

module.exports = {
minimizer: [ImageMinimizer],
};
64 changes: 52 additions & 12 deletions config/webpack/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,77 @@ const path = require('path');
const webpack = require('webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const _MiniCssExtractPlugin = require('mini-css-extract-plugin');
const _ImageminPlugin = require('imagemin-webpack-plugin').default;
const _SpriteLoaderPlugin = require('svg-sprite-loader/plugin');
const CopyPlugin = require('copy-webpack-plugin');
const glob = require('glob');
const fs = require('fs-extra');

const imagePath = path.resolve(__dirname, '../../../../../assets/images');
// Get directories for file contexts.
const projectDir = path.resolve(__dirname, '../../../../..');
const srcDir = path.resolve(projectDir, 'src');

// Emulsify project configuration.
const emulsifyConfig = require('../../../../../project.emulsify.json');

// Compress images plugin.
const MiniCssExtractPlugin = new _MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css',
});

const ImageminPlugin = new _ImageminPlugin({
disable: process.env.NODE_ENV !== 'production',
externalImages: {
context: imagePath,
sources: glob.sync(path.resolve(imagePath, '**/*.{png,jpg,gif,svg}')),
destination: imagePath,
},
});

// Create SVG sprite.
const SpriteLoaderPlugin = new _SpriteLoaderPlugin({
plainSprite: true,
});

// Enable Webpack progress plugin.
const ProgressPlugin = new webpack.ProgressPlugin();

// Glob pattern for markup files.
const componentFilesPattern = path.resolve(srcDir, '**/*.{twig,yml}');

/**
* Prepare list of twig files to copy to "compiled" directories.
* @constructor
* @param {string} filesMatcher - Glob pattern.
*/
function getPatterns(filesMatcher) {
const patterns = [];
glob.sync(filesMatcher).forEach((file) => {
const projectPath = file.split('/src/')[0];
const srcStructure = file.split(`${srcDir}/`)[1];
const parentDir = srcStructure.split('/')[0];
const filePath = file.split(/(foundation\/|components\/|layout\/)/)[2];
const consolidateDirs =
parentDir === 'layout' || parentDir === 'foundation'
? '/components/'
: '/';
const newfilePath =
emulsifyConfig.project.platform === 'drupal'
? `${projectPath}${consolidateDirs}${parentDir}/${filePath}`
: `${projectPath}/dist/${parentDir}/${filePath}`;
patterns.push({
from: file,
to: newfilePath,
});
});

return patterns;
}

// Copy twig files from src directory.
const CopyTwigPlugin = fs.existsSync(path.resolve(projectDir, 'src'))
? new CopyPlugin({
patterns: getPatterns(componentFilesPattern),
})
: '';

// Export plugin configuration.
module.exports = {
ProgressPlugin,
MiniCssExtractPlugin,
ImageminPlugin,
SpriteLoaderPlugin,
CopyTwigPlugin,
CleanWebpackPlugin: new CleanWebpackPlugin({
protectWebpackAssets: false, // Required for removal of extra, unwanted dist/css/*.js files.
cleanOnceBeforeBuildPatterns: ['!*.{png,jpg,gif,svg}'],
Expand Down
79 changes: 79 additions & 0 deletions config/webpack/resolves.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const path = require('path');
const glob = require('glob');
const fs = require('fs-extra');

// Emulsify project configuration.
const emulsifyConfig = require('../../../../../project.emulsify.json');

// Get directories for file contexts.
const projectDir = path.resolve(__dirname, '../../../../..');
const projectName = emulsifyConfig.project.name;
const srcDir = fs.existsSync(path.resolve(projectDir, 'src'))
? path.resolve(projectDir, 'src')
: path.resolve(projectDir, 'components');

// Glob pattern for twig aliases.
const aliasPattern = path.resolve(srcDir, '**/!(_*).twig');

/**
* Return all top-level directories from the projects source directory.
* @constructor
* @param {string} source - Path to source directory.
*/
function getDirectories(source) {
const dirs = fs

Check warning on line 24 in config/webpack/resolves.js

View workflow job for this annotation

GitHub Actions / build

Found readdirSync from package "fs-extra" with non literal argument at index 0
.readdirSync(source, { withFileTypes: true }) // Read contents of the directory
.filter((dirent) => dirent.isDirectory()) // Filter only directories
.map((dirent) => dirent.name);
return dirs;
}

/**
* Return clean directory names if numbering is used for sorting.
* @constructor
* @param {string} dir - Given directory name.
*/
function cleanDirectoryName(dir) {
if (/^\d{2}/.test(dir)) {
return dir.slice(3);
}
return dir;
}

/**
* Return a list of twig file file paths from the project source directory.
* @constructor
* @param {string} aliasMatcher - Glob pattern.
*/
function getAliases(aliasMatcher) {
// Create default aliases
let aliases = {};
// Add SDC compatible aliases.
glob.sync(aliasMatcher).forEach((file) => {
const filePath = file.split(`${srcDir}/`)[1];
const fileName = path.basename(filePath);

if (emulsifyConfig.project.platform === 'drupal') {
aliases[`${projectName}/${fileName.replace('.twig', '')}`] = file;
}
});
// Add typical @namespace (path to directory) aliases for twig partials.
const dirs = getDirectories(srcDir);
dirs.forEach((dir) => {
const name = cleanDirectoryName(dir);
Object.assign(aliases, {
[`@${name}`]: `${projectDir}/${path.basename(srcDir)}/${dir}`,
});
});
return aliases;
}

// Alias twig namespaces.
const TwigResolve = {
extensions: ['.twig'],
alias: getAliases(aliasPattern),
};

module.exports = {
TwigResolve,
};
8 changes: 8 additions & 0 deletions config/webpack/sdc-loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = function (source) {
const projectName = this.getOptions().projectName || '';
const result = source.replace(
new RegExp(`${projectName}:`, 'g'),

Check warning on line 4 in config/webpack/sdc-loader.js

View workflow job for this annotation

GitHub Actions / build

Found non-literal argument to RegExp Constructor
`${projectName}/`,
);
return result;
};
Loading

0 comments on commit c61f703

Please sign in to comment.