Skip to content

Commit

Permalink
Update svgr and use project svgo version
Browse files Browse the repository at this point in the history
  • Loading branch information
devongovett committed Nov 12, 2024
1 parent 584b9b3 commit 71acf87
Show file tree
Hide file tree
Showing 4 changed files with 329 additions and 142 deletions.
81 changes: 80 additions & 1 deletion packages/core/integration-tests/test/svg-react.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import assert from 'assert';
import {bundle, outputFS} from '@parcel/test-utils';
import {bundle, outputFS, fsFixture, overlayFS} from '@parcel/test-utils';
import path from 'path';
import Logger from '@parcel/logger';
import {md} from '@parcel/diagnostic';

describe('svg-react', function () {
it('should support transforming SVGs to react components', async function () {
Expand Down Expand Up @@ -56,4 +58,81 @@ describe('svg-react', function () {
assert(file.includes('(0, _preact.h)("svg"'));
assert(file.includes('width: "1em"'));
});

it('should detect the version of SVGO to use', async function () {
// Test is outside parcel so that svgo is not already installed.
await fsFixture(overlayFS, '/')`
svgr-svgo-version
icon.svg:
<svg></svg>
index.html:
<img src="icon.svg" />
svgo.config.json:
{
"full": true
}
yarn.lock:
`;

let messages = [];
let loggerDisposable = Logger.onLog(message => {
if (message.level !== 'verbose') {
messages.push(message);
}
});

try {
await bundle(path.join('/svgr-svgo-version/index.html'), {
inputFS: overlayFS,
defaultTargetOptions: {
shouldOptimize: true,
},
shouldAutoinstall: false,
defaultConfig: path.join(
__dirname,
'integration/custom-configs/.parcelrc-svg-react',
),
});
} catch (err) {
// autoinstall is disabled
assert.equal(
err.diagnostics[0].message,
md`Could not resolve module "svgo" from "${path.resolve(
overlayFS.cwd(),
'/svgr-svgo-version/index',
)}"`,
);
}

loggerDisposable.dispose();
assert(
messages[0].diagnostics[0].message.startsWith(
'Detected deprecated SVGO v2 options in',
),
);
assert.deepEqual(messages[0].diagnostics[0].codeFrames, [
{
filePath: path.resolve(
overlayFS.cwd(),
'/svgr-svgo-version/svgo.config.json',
),
codeHighlights: [
{
message: undefined,
start: {
line: 2,
column: 3,
},
end: {
line: 2,
column: 14,
},
},
],
},
]);
});
});
7 changes: 4 additions & 3 deletions packages/transformers/svg-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@
"parcel": "^2.12.0"
},
"dependencies": {
"@parcel/diagnostic": "2.12.0",
"@parcel/plugin": "2.12.0",
"@svgr/core": "^6.2.0",
"@svgr/plugin-jsx": "^6.2.0",
"@svgr/plugin-svgo": "^6.2.0"
"@parcel/utils": "2.12.0",
"@svgr/core": "^8.0.0",
"@svgr/plugin-jsx": "^8.0.0"
}
}
136 changes: 130 additions & 6 deletions packages/transformers/svg-react/src/SvgReactTransformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import {Transformer} from '@parcel/plugin';

import svgoPlugin from '@svgr/plugin-svgo';
import jsxPlugin from '@svgr/plugin-jsx';
import {transform} from '@svgr/core';
import {detectSVGOVersion} from '@parcel/utils';
import path from 'path';
import {md, generateJSONCodeHighlights} from '@parcel/diagnostic';

export default (new Transformer({
async loadConfig({config}) {
async loadConfig({config, logger, options}) {
let svgrResult = await config.getConfig([
'.svgrrc',
'.svgrrc.json',
Expand All @@ -19,25 +21,102 @@ export default (new Transformer({
'svgr.config.cjs',
'svgr.config.mjs',
]);
let svgoResult = await config.getConfig([
let svgoResult: any = await config.getConfig([
'svgo.config.js',
'svgo.config.cjs',
'svgo.config.mjs',
'svgo.config.json',
]);
return {svgr: svgrResult?.contents, svgo: svgoResult?.contents};

let svgoConfig = svgrResult?.contents?.svgoConfig ?? svgoResult?.contents;
let svgoConfigPath = svgrResult?.contents?.svgoConfig
? svgrResult.filePath
: svgoResult?.filePath;

// See if svgo is already installed.
let resolved;
try {
resolved = await options.packageManager.resolve(
'svgo',
path.join(options.projectRoot, 'index'),
{shouldAutoInstall: false},
);
} catch (err) {
// ignore.
}

// If so, use the existing installed version.
let svgoVersion = 3;
if (resolved) {
if (resolved.pkg?.version) {
svgoVersion = parseInt(resolved.pkg.version);
}
} else if (svgoConfig) {
// Otherwise try to detect the version based on the config file.
let v = detectSVGOVersion(svgoConfig);
if (svgoConfig != null && v.version === 2) {
logger.warn({
message: md`Detected deprecated SVGO v2 options in ${path.relative(
process.cwd(),
svgoConfigPath,
)}`,
codeFrames: [
{
filePath: svgoConfigPath,
codeHighlights:
path.basename(svgoConfigPath) === '.svgrrc' ||
path.extname(svgoConfigPath) === '.json'
? generateJSONCodeHighlights(
await options.inputFS.readFile(svgoConfigPath, 'utf8'),
[
{
key: `${
svgrResult?.contents?.svgoConfig
? '/svgoConfig'
: ''
}${v.path}`,
},
],
)
: [],
},
],
});
}

svgoVersion = v.version;
}

return {svgr: svgrResult?.contents, svgo: svgoConfig, svgoVersion};
},

async transform({asset, config}) {
async transform({asset, config, options}) {
let code = await asset.getCode();

let plugins = [];
if (config.svgr?.svgo !== false) {
let svgo = await options.packageManager.require(
'svgo',
path.join(options.projectRoot, 'index'),
{
range: `^${config.svgoVersion}`,
saveDev: true,
shouldAutoInstall: options.shouldAutoInstall,
},
);

plugins.push(createSvgoPlugin(svgo));
}

plugins.push(jsxPlugin);

const jsx = await transform(
code,
{svgoConfig: config.svgo, ...config.svgr, runtimeConfig: false},
{
caller: {
name: '@parcel/transformer-svg-react',
defaultPlugins: [svgoPlugin, jsxPlugin],
defaultPlugins: plugins,
},
filePath: asset.filePath,
},
Expand All @@ -50,3 +129,48 @@ export default (new Transformer({
return [asset];
},
}): Transformer);

// Below is copied from @svgr/plugin-svgo. MIT license.
// https://github.com/gregberge/svgr/tree/180eb6d503215fc782dfece351ff751194a0dfed/packages/plugin-svgo

function getSvgoConfigFromSvgrConfig(config) {
const params = {overrides: {}};
if (config.icon || config.dimensions === false) {
params.overrides.removeViewBox = false;
}
if (config.native) {
params.overrides.inlineStyles = {
onlyMatchedOnce: false,
};
}

return {
plugins: [
{
name: 'preset-default',
params,
},
'prefixIds',
],
};
}

function getSvgoConfig(config) {
if (config.svgoConfig) return config.svgoConfig;
return getSvgoConfigFromSvgrConfig(config);
}

function createSvgoPlugin(svgo) {
return (code, config, state) => {
const svgoConfig = getSvgoConfig(config);
const result = svgo.optimize(code, {...svgoConfig, path: state.filePath});

// @ts-ignore
if (result.modernError) {
// @ts-ignore
throw result.modernError;
}

return result.data;
};
}
Loading

0 comments on commit 71acf87

Please # to comment.