Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* Use node-native glob functionality in `translate compile`. Refs STCLI-273.
* Bug fix: `app create` cleans up correct `.git` directory; tests correctly await slow git-clones. Refs STCLI-293.
* Migrate from `eslint` to `oxlint`. Refs STCLI-290.
* *BREAKING* bump `@folio/stripes-webpack` to v8, remove `babelOptions` public export and orphaned `@folio/stripes-testing` dependency. Refs STCLI-292.

## [5.0.0](https://github.com/folio-org/stripes-cli/tree/v5.0.0) (2026-04-15)
[Full Changelog](https://github.com/folio-org/stripes-cli/compare/v4.0.0...v5.0.0)
Expand Down
6 changes: 0 additions & 6 deletions index.js

This file was deleted.

22 changes: 22 additions & 0 deletions lib/istanbul-loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// webpack loader that adds istanbul coverage instrumentation to already-transpiled code.
// it is meant to run as a post-loader, i.e. after esbuild-loader, which has no coverage
// support of its own: https://github.com/evanw/esbuild/issues/184
const { createInstrumenter } = require('istanbul-lib-instrument');

const instrumenter = createInstrumenter({
esModules: true,
compact: false,
produceSourceMap: true,
autoWrap: true,
});

module.exports = function istanbulLoader(source, inputSourceMap) {
const callback = this.async();
instrumenter.instrument(source, this.resourcePath, (err, code) => {
if (err) {
callback(err);
} else {
callback(null, code, instrumenter.lastSourceMap() || inputSourceMap);
}
}, inputSourceMap || undefined);
};
2 changes: 1 addition & 1 deletion lib/test/webpack-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ module.exports = function getStripesWebpackConfig(stripeCore, stripesConfig, opt
config.resolve.modules = ['node_modules', platformModulePath, coreModulePath];
config.resolveLoader = { modules: ['node_modules', platformModulePath, coreModulePath] };

// Inject babel-plugin-istanbul when coverage is enabled
// Add the istanbul loader when coverage is enabled
if (options.coverage) {
enableCoverage(config);
}
Expand Down
47 changes: 17 additions & 30 deletions lib/webpack-common.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const fs = require('fs');
const path = require('path');
const { set } = require('lodash');
const webpack = require('webpack');
const logger = require('./cli/logger')('webpack');

Expand Down Expand Up @@ -104,39 +103,27 @@ function shouldModuleBeIncluded(modulePath) {
return !nodeModulesRegex.test(modulePath);
}

// test coverage is activated by including the instrumentation plugin to the babel plugin stack.
// the test stack currently includes es-build for dev and production, but unfortunately, the tool includes
// test coverage is activated by adding an istanbul instrumentation loader after the transpiler.
// the test stack includes es-build for dev and production, but unfortunately, the tool includes
// no test coverage instrumentation :o( https://github.com/evanw/esbuild/issues/184

function enableCoverage(config) {
const babelLoaderConfigIndex = config.module.rules.findIndex((rule) => {
return rule?.oneOf?.[1]?.use?.[0].loader === 'babel-loader';
});

if (!config.module.rules[babelLoaderConfigIndex]?.oneOf?.[1].use[0].options?.plugins) {
set(config.module.rules[babelLoaderConfigIndex], 'oneOf[1].use[0].options.plugins', []);
}
// exclude node_modules (shouldModuleBeIncluded lets @folio modules through so they get transpiled,
// but they should not be in coverage reports) and test and story files from coverage reports here.
const excludeRegex = [
/[\\/]node_modules[\\/]/,
/\.test\.js$/,
/[\\/]tests[\\/][^\\/]+\.js$/,
/[\\/]stories[\\/][^\\/]+\.js$/,
];

// only use babel configuration for test coverage..
const babelConfigurationItem = config.module.rules[babelLoaderConfigIndex].oneOf[1];

// exclude files from coverage reports here.
config.module.rules[babelLoaderConfigIndex].oneOf[1].use[0].options.plugins.push(
[require.resolve('babel-plugin-istanbul'), {
exclude: [
'**/*.test.js',
'**/tests/*.js',
'**/stories/*.js',
'/node_modules/*'
]
}]
);

babelConfigurationItem.test = /\.js$/;
babelConfigurationItem.include = shouldModuleBeIncluded;

// replace the esbuild tooling with babel-loader
set(config.module, `rules[${babelLoaderConfigIndex}]`, babelConfigurationItem);
config.module.rules.push({
test: /\.js$/,
enforce: 'post',
include: shouldModuleBeIncluded,
exclude: excludeRegex,
loader: require.resolve('./istanbul-loader'),
});

return config;
}
Expand Down
6 changes: 2 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
"engines": {
"node": ">=22.12.0"
},
"main": "index.js",
"bin": {
"stripes": "./lib/stripes-cli.js"
},
Expand All @@ -21,12 +20,10 @@
"docs": "node ./lib/doc/generator"
},
"dependencies": {
"@folio/stripes-testing": "^3.0.0",
"@folio/stripes-webpack": "^7.0.0",
"@folio/stripes-webpack": "^8.0.0",
"@formatjs/cli": "^6.1.3",
"@formatjs/cli-lib": "^6.1.3",
"@octokit/rest": "^22.0.1",
"babel-plugin-istanbul": "^6.0.0",
"configstore": "^3.1.1",
"debug": "^4.0.1",
"express": "^4.17.1",
Expand All @@ -40,6 +37,7 @@
"inquirer": "^8.2.0",
"is-installed-globally": "^0.4.0",
"is-path-inside": "^1.0.1",
"istanbul-lib-instrument": "^6.0.3",
"istanbul-reports": "^3.1.4",
"just-kebab-case": "^4.2.0",
"just-pascal-case": "^1.0.0",
Expand Down
39 changes: 39 additions & 0 deletions test/lib/istanbul-loader.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const expect = require('chai').expect;

const istanbulLoader = require('../../lib/istanbul-loader');

// run the loader the way webpack would, resolving with its (err, code, map) callback args
function runLoader(source, inputSourceMap) {
return new Promise((resolve) => {
const context = {
resourcePath: '/project/src/example.js',
async: () => (err, code, map) => resolve({ err, code, map }),
};
istanbulLoader.call(context, source, inputSourceMap);
});
}

describe('The istanbul loader', function () {
it('adds coverage counters to ES module source', async function () {
const { err, code } = await runLoader('export const add = (a, b) => a + b;');

expect(err).to.equal(null);
expect(code).to.include('__coverage__');
expect(code).to.include('/project/src/example.js');
expect(code).to.include('export const add');
});

it('emits a source map', async function () {
const { map } = await runLoader('export const add = (a, b) => a + b;');

expect(map).to.be.an('object');
expect(map).to.have.property('mappings');
});

it('passes syntax errors to the callback', async function () {
const { err, code } = await runLoader('export const = ;');

expect(err).to.be.an('error');
expect(code).to.equal(undefined);
});
});
Loading
Loading