@pharos-ds/build 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@pharos-ds/build",
3
+ "version": "0.1.9",
4
+ "description": "Build plugins for XDS source builds — babel, PostCSS, and Vite integrations",
5
+ "author": "Caio Silvestre",
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/Caio-Silvestre/alexandria-ds#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/Caio-Silvestre/alexandria-ds.git",
11
+ "directory": "packages/build"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/Caio-Silvestre/alexandria-ds/issues"
15
+ },
16
+ "keywords": [
17
+ "pharos",
18
+ "postcss",
19
+ "stylex",
20
+ "css-layers",
21
+ "design-system"
22
+ ],
23
+ "main": "./src/config.js",
24
+ "exports": {
25
+ ".": "./src/config.js",
26
+ "./babel": "./src/babel.js",
27
+ "./postcss": "./src/index.js",
28
+ "./vite": {
29
+ "types": "./dist/vite.d.ts",
30
+ "import": "./dist/vite.mjs"
31
+ },
32
+ "./next": "./src/next.js"
33
+ },
34
+ "files": [
35
+ "src",
36
+ "dist"
37
+ ],
38
+ "peerDependencies": {
39
+ "@stylexjs/babel-plugin": ">=0.18.0",
40
+ "@babel/core": ">=7.0.0",
41
+ "@stylexjs/unplugin": ">=0.18.0",
42
+ "vite": "^8.1.3"
43
+ },
44
+ "dependencies": {
45
+ "fast-glob": "^3.3.0",
46
+ "glob-parent": "^6.0.0",
47
+ "is-glob": "^4.0.0"
48
+ },
49
+ "devDependencies": {
50
+ "esbuild": "^0.28.1"
51
+ },
52
+ "peerDependenciesMeta": {
53
+ "@stylexjs/unplugin": {
54
+ "optional": true
55
+ },
56
+ "vite": {
57
+ "optional": true
58
+ }
59
+ },
60
+ "scripts": {
61
+ "build": "node build.mjs"
62
+ }
63
+ }
package/src/babel.js ADDED
@@ -0,0 +1,106 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ "use strict";
4
+
5
+ /**
6
+ * @pharos-ds/build/babel
7
+ *
8
+ * Babel plugin that delegates to @stylexjs/babel-plugin with a
9
+ * different classNamePrefix for Pharos library files vs product files.
10
+ *
11
+ * Library files get 'pharos' prefix (.pharos78zum5), product files get 'x' (.x78zum5).
12
+ */
13
+
14
+ const LIBRARY_PATTERNS = [
15
+ 'packages/core/',
16
+ 'packages/themes/',
17
+ 'packages/lab/',
18
+ 'node_modules/@pharos-ds/',
19
+ ];
20
+
21
+ module.exports = function pharosBabelPlugin(api, options) {
22
+ const stylexPlugin = require('@stylexjs/babel-plugin');
23
+
24
+ const {
25
+ libraryPatterns = LIBRARY_PATTERNS,
26
+ libraryPrefix = 'pharos',
27
+ classNamePrefix = 'x',
28
+ ...stylexOptions
29
+ } = options;
30
+
31
+ // Build the two sets of options — only classNamePrefix differs
32
+ const libraryOpts = {...stylexOptions, classNamePrefix: libraryPrefix};
33
+ const productOpts = {...stylexOptions, classNamePrefix};
34
+
35
+ // Create two plugin instances
36
+ const libraryPlugin = stylexPlugin(api, libraryOpts);
37
+ const productPlugin = stylexPlugin(api, productOpts);
38
+
39
+ function getPluginAndOpts(filename) {
40
+ const isLibrary = libraryPatterns.some(p => filename.includes(p));
41
+ return isLibrary
42
+ ? {plugin: libraryPlugin, opts: libraryOpts}
43
+ : {plugin: productPlugin, opts: productOpts};
44
+ }
45
+
46
+ // Helper: call a visitor function with overridden state.opts
47
+ function callVisitor(visitor, key, path, state) {
48
+ const {plugin, opts} = getPluginAndOpts(state.filename || '');
49
+ const v = plugin.visitor[key];
50
+ if (!v) return;
51
+
52
+ // Override state.opts so StyleX reads the correct classNamePrefix
53
+ const originalOpts = state.opts;
54
+ state.opts = opts;
55
+
56
+ try {
57
+ if (typeof v === 'function') {
58
+ v.call(this, path, state);
59
+ } else if (typeof v === 'object') {
60
+ // enter/exit form — should not happen at top level for stylex
61
+ // but handle it anyway
62
+ if (visitor === 'enter' && v.enter) v.enter.call(this, path, state);
63
+ if (visitor === 'exit' && v.exit) v.exit.call(this, path, state);
64
+ }
65
+ } finally {
66
+ state.opts = originalOpts;
67
+ }
68
+ }
69
+
70
+ // Get all visitor keys
71
+ const allKeys = new Set([
72
+ ...Object.keys(libraryPlugin.visitor || {}),
73
+ ...Object.keys(productPlugin.visitor || {}),
74
+ ]);
75
+
76
+ const visitor = {};
77
+
78
+ for (const key of allKeys) {
79
+ const sample = libraryPlugin.visitor[key] || productPlugin.visitor[key];
80
+
81
+ if (typeof sample === 'object' && (sample.enter || sample.exit)) {
82
+ // enter/exit form (Program uses this)
83
+ visitor[key] = {};
84
+ if (sample.enter) {
85
+ visitor[key].enter = function(path, state) {
86
+ callVisitor.call(this, 'enter', key, path, state);
87
+ };
88
+ }
89
+ if (sample.exit) {
90
+ visitor[key].exit = function(path, state) {
91
+ callVisitor.call(this, 'exit', key, path, state);
92
+ };
93
+ }
94
+ } else {
95
+ // Simple function visitor
96
+ visitor[key] = function(path, state) {
97
+ callVisitor.call(this, 'enter', key, path, state);
98
+ };
99
+ }
100
+ }
101
+
102
+ return {
103
+ name: 'pharos-babel-plugin',
104
+ visitor,
105
+ };
106
+ };
@@ -0,0 +1,73 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file babel.test.mjs
5
+ * @description Verifies that the XDS babel wrapper applies the configured
6
+ * library StyleX class-name prefix to XDS library files. Part of the
7
+ * the library atom prefix defaults to `pharos` and is configurable
8
+ * before the final cutover.
9
+ */
10
+
11
+ import {describe, it, expect} from 'vitest';
12
+ import {createRequire} from 'node:module';
13
+
14
+ const require = createRequire(import.meta.url);
15
+ const babel = require('@babel/core');
16
+ const pharosBabelPlugin = require('./babel.js');
17
+
18
+ const SOURCE = `
19
+ import * as stylex from '@stylexjs/stylex';
20
+ export const styles = stylex.create({
21
+ box: {color: 'red'},
22
+ });
23
+ `;
24
+
25
+ /**
26
+ * Transform a StyleX source through the XDS babel wrapper as if it were a
27
+ * library file, returning the emitted code. `libraryPrefix` controls the
28
+ * atomic class-name prefix for library files.
29
+ */
30
+ function transformLibraryFile(libraryPrefix) {
31
+ const result = babel.transformSync(SOURCE, {
32
+ // A path matching one of the library patterns so the wrapper routes it
33
+ // through the library plugin instance.
34
+ filename: 'node_modules/@pharos-ds/core/src/Box/XDSBox.tsx',
35
+ babelrc: false,
36
+ configFile: false,
37
+ plugins: [
38
+ [
39
+ pharosBabelPlugin,
40
+ {
41
+ ...(libraryPrefix ? {libraryPrefix} : {}),
42
+ unstable_moduleResolution: {type: 'commonJS', rootDir: process.cwd()},
43
+ },
44
+ ],
45
+ ],
46
+ });
47
+ return result?.code ?? '';
48
+ }
49
+
50
+ /** Extract the StyleX atomic class names referenced in the emitted code. */
51
+ function atomicClasses(code) {
52
+ // StyleX emits atoms like "xds1a2b3c" / "pharos1a2b3c" in the compiled output.
53
+ return code.match(/\b(?:xds|pharos|lib)[a-z0-9]{4,}\b/g) ?? [];
54
+ }
55
+
56
+ describe('xds babel wrapper -- library StyleX prefix', () => {
57
+ it('defaults library atoms to the `pharos` prefix', () => {
58
+ const code = transformLibraryFile(undefined);
59
+ const atoms = atomicClasses(code);
60
+ expect(atoms.length).toBeGreaterThan(0);
61
+ expect(atoms.every(c => c.startsWith('pharos'))).toBe(true);
62
+ expect(
63
+ atoms.some(c => c.startsWith('xds') && !c.startsWith('pharos')),
64
+ ).toBe(false);
65
+ });
66
+
67
+ it('uses a configured custom prefix for library atoms', () => {
68
+ const code = transformLibraryFile('lib');
69
+ const atoms = atomicClasses(code);
70
+ expect(atoms.length).toBeGreaterThan(0);
71
+ expect(atoms.every(c => c.startsWith('lib'))).toBe(true);
72
+ });
73
+ });
package/src/config.js ADDED
@@ -0,0 +1,92 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ "use strict";
4
+
5
+ /**
6
+ * @pharos-ds/build
7
+ *
8
+ * Unified build configuration for Pharos source builds.
9
+ *
10
+ * Usage:
11
+ * // babel.config.js
12
+ * const {babel} = require('@pharos-ds/build');
13
+ * module.exports = babel(__dirname);
14
+ *
15
+ * // postcss.config.js
16
+ * const {postcss} = require('@pharos-ds/build');
17
+ * module.exports = postcss(__dirname);
18
+ */
19
+
20
+ const path = require('node:path');
21
+
22
+ /**
23
+ * Resolve Pharos package aliases from a root directory.
24
+ * Handles both npm installs (node_modules/@pharos-ds/core) and
25
+ * monorepo layouts (packages/core).
26
+ */
27
+ function resolveAliases(rootDir) {
28
+ const coreDir = path.join(rootDir, 'node_modules/@pharos-ds/core');
29
+ return {
30
+ '@pharos-ds/core/*': [path.join(coreDir, '*')],
31
+ '@pharos-ds/core': [coreDir],
32
+ };
33
+ }
34
+
35
+ /**
36
+ * Build shared StyleX babel plugin options from a root directory.
37
+ */
38
+ function stylexOptions(rootDir, overrides = {}) {
39
+ return {
40
+ dev: process.env.NODE_ENV !== 'production',
41
+ runtimeInjection: false,
42
+ enableInlinedConditionalMerge: true,
43
+ treeshakeCompensation: true,
44
+ aliases: resolveAliases(rootDir),
45
+ unstable_moduleResolution: {
46
+ type: 'commonJS',
47
+ },
48
+ ...overrides,
49
+ };
50
+ }
51
+
52
+ /**
53
+ * Generate a complete babel.config.js for Pharos source builds.
54
+ *
55
+ * @param {string} rootDir — __dirname of the project root
56
+ * @param {object} [overrides] — extra StyleX options to merge
57
+ * @returns {object} babel config object
58
+ */
59
+ function babel(rootDir, overrides = {}) {
60
+ return {
61
+ presets: ['next/babel'],
62
+ plugins: [
63
+ [require.resolve('./babel.js'), stylexOptions(rootDir, overrides)],
64
+ ],
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Generate a complete postcss.config.js for Pharos source builds.
70
+ *
71
+ * @param {string} rootDir — __dirname of the project root
72
+ * @param {object} [overrides] — extra options (appDir, extraInclude, etc.)
73
+ * @returns {object} postcss config object
74
+ */
75
+ function postcss(rootDir, overrides = {}) {
76
+ const {appDir = 'src', extraInclude = [], ...rest} = overrides;
77
+ return {
78
+ plugins: {
79
+ [require.resolve('./index.js')]: {
80
+ cwd: rootDir,
81
+ appDir,
82
+ babelPlugins: [
83
+ ['@stylexjs/babel-plugin', stylexOptions(rootDir, rest)],
84
+ ],
85
+ extraInclude,
86
+ },
87
+ autoprefixer: {},
88
+ },
89
+ };
90
+ }
91
+
92
+ module.exports = {babel, postcss, stylexOptions, resolveAliases};
package/src/index.js ADDED
@@ -0,0 +1,251 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ "use strict";
4
+
5
+ /**
6
+ * @pharos-ds/postcss-plugin
7
+ *
8
+ * PostCSS plugin for Pharos source builds. Compiles StyleX from both
9
+ * Pharos library source and product code in two separate passes with
10
+ * different class name prefixes, then outputs them in separate layers:
11
+ *
12
+ * reset < pharos-base (library, prefix: 'pharos') < pharos-theme < product (prefix: 'x')
13
+ *
14
+ * The separate prefixes ensure atomic classes don't collide between
15
+ * layers, which would break theme overrides.
16
+ */
17
+
18
+ const path = require('node:path');
19
+ const fs = require('node:fs');
20
+ const postcss = require('postcss');
21
+ const babel = require('@babel/core');
22
+ const stylexBabelPlugin = require('@stylexjs/babel-plugin');
23
+ const {globSync} = require('fast-glob');
24
+ const isGlob = require('is-glob');
25
+ const globParent = require('glob-parent');
26
+
27
+ const PLUGIN_NAME = '@pharos-ds/postcss-plugin';
28
+
29
+ const LIBRARY_GLOB = 'node_modules/@pharos-ds/**/*.{ts,tsx}';
30
+ const LIBRARY_PATTERNS = ['node_modules/@pharos-ds/', 'packages/core/', 'packages/themes/'];
31
+ const STYLEX_IMPORT_SOURCE = '@stylexjs/stylex';
32
+
33
+ function parseDependency(fileOrGlob, cwd) {
34
+ if (fileOrGlob.startsWith('!')) return null;
35
+ if (isGlob(fileOrGlob)) {
36
+ const base = globParent(fileOrGlob);
37
+ let glob = fileOrGlob.substring(base === '.' ? 0 : base.length);
38
+ if (glob.charAt(0) === '/') glob = glob.substring(1);
39
+ return {
40
+ type: 'dir-dependency',
41
+ dir: path.normalize(path.resolve(cwd, base)),
42
+ glob,
43
+ };
44
+ }
45
+ return {
46
+ type: 'dependency',
47
+ file: path.normalize(path.resolve(cwd, fileOrGlob)),
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Clone a babel plugins array, overriding the classNamePrefix on the
53
+ * StyleX babel plugin entry.
54
+ */
55
+ function withClassNamePrefix(plugins, prefix) {
56
+ return plugins.map(p => {
57
+ if (!Array.isArray(p)) return p;
58
+ const [pluginPath, opts] = p;
59
+ // Match @stylexjs/babel-plugin by name
60
+ const name = typeof pluginPath === 'string' ? pluginPath : '';
61
+ if (name.includes('@stylexjs/babel-plugin') || name.includes('stylex')) {
62
+ return [pluginPath, {...opts, classNamePrefix: prefix}];
63
+ }
64
+ return p;
65
+ });
66
+ }
67
+
68
+ function createPlugin() {
69
+ const isDev = process.env.NODE_ENV === 'development';
70
+
71
+ // Separate rule maps for library and product
72
+ const libraryRulesMap = new Map();
73
+ const productRulesMap = new Map();
74
+ const fileModifiedMap = new Map();
75
+
76
+ const plugin = ({
77
+ cwd = process.cwd(),
78
+ appDir = 'src',
79
+ babelPlugins = [],
80
+ layers = {
81
+ library: 'pharos-base',
82
+ product: 'product',
83
+ },
84
+ // Class name prefix for library styles (product keeps default 'x')
85
+ libraryPrefix = 'pharos',
86
+ extraInclude = [],
87
+ libraryPatterns = LIBRARY_PATTERNS,
88
+ exclude = [],
89
+ }) => {
90
+ const include = [
91
+ `${appDir}/**/*.{js,jsx,ts,tsx}`,
92
+ LIBRARY_GLOB,
93
+ ...extraInclude,
94
+ ];
95
+
96
+ const excludeWithDefaults = ['**/*.d.ts', '**/*.flow', ...exclude];
97
+
98
+ // Two babel configs — different classNamePrefix
99
+ const libraryBabelConfig = {
100
+ babelrc: false,
101
+ parserOpts: {plugins: ['typescript', 'jsx']},
102
+ plugins: withClassNamePrefix(babelPlugins, libraryPrefix),
103
+ };
104
+
105
+ const productBabelConfig = {
106
+ babelrc: false,
107
+ parserOpts: {plugins: ['typescript', 'jsx']},
108
+ plugins: babelPlugins, // keeps default 'x' prefix
109
+ };
110
+
111
+ let shouldSkipTransformError = false;
112
+
113
+ return {
114
+ postcssPlugin: PLUGIN_NAME,
115
+ plugins: [
116
+ async function (root, result) {
117
+ const fileName = result.opts.from;
118
+
119
+ let styleXAtRule = null;
120
+ root.walkAtRules((atRule) => {
121
+ if (atRule.name === 'stylex' && !atRule.params) {
122
+ styleXAtRule = atRule;
123
+ }
124
+ });
125
+ if (styleXAtRule == null) return;
126
+
127
+ // Discover files
128
+ const files = new Set();
129
+ for (const pattern of include) {
130
+ const matched = globSync(pattern, {
131
+ onlyFiles: true,
132
+ ignore: excludeWithDefaults,
133
+ cwd,
134
+ });
135
+ for (const f of matched) {
136
+ files.add(path.normalize(path.resolve(cwd, f)));
137
+ }
138
+ }
139
+
140
+ // Watch dependencies
141
+ for (const pattern of include) {
142
+ const dep = parseDependency(pattern, cwd);
143
+ if (dep) {
144
+ result.messages.push({
145
+ plugin: PLUGIN_NAME,
146
+ parent: fileName,
147
+ ...dep,
148
+ });
149
+ }
150
+ }
151
+
152
+ // Remove deleted files
153
+ for (const f of fileModifiedMap.keys()) {
154
+ if (!files.has(f)) {
155
+ fileModifiedMap.delete(f);
156
+ libraryRulesMap.delete(f);
157
+ productRulesMap.delete(f);
158
+ }
159
+ }
160
+
161
+ // Partition files into library vs product, then compile
162
+ // each group with its own babel config (different prefix)
163
+ const transforms = [];
164
+ for (const filePath of files) {
165
+ const mtimeMs = fs.existsSync(filePath)
166
+ ? fs.statSync(filePath).mtimeMs
167
+ : -Infinity;
168
+ if (
169
+ fileModifiedMap.has(filePath) &&
170
+ mtimeMs === fileModifiedMap.get(filePath)
171
+ ) {
172
+ continue;
173
+ }
174
+ fileModifiedMap.set(filePath, mtimeMs);
175
+
176
+ const contents = fs.readFileSync(filePath, 'utf-8');
177
+ if (!contents.includes(STYLEX_IMPORT_SOURCE)) continue;
178
+
179
+ const isLibrary = libraryPatterns.some(p => filePath.includes(p));
180
+ const config = isLibrary ? libraryBabelConfig : productBabelConfig;
181
+ const rulesMap = isLibrary ? libraryRulesMap : productRulesMap;
182
+
183
+ transforms.push(
184
+ babel
185
+ .transformAsync(contents, {
186
+ filename: filePath,
187
+ caller: {name: PLUGIN_NAME, platform: 'web', isDev},
188
+ ...config,
189
+ })
190
+ .then(({metadata}) => {
191
+ const stylex = metadata?.stylex;
192
+ if (stylex != null && stylex.length > 0) {
193
+ rulesMap.set(filePath, stylex);
194
+ }
195
+ })
196
+ .catch((error) => {
197
+ if (shouldSkipTransformError) {
198
+ console.warn(
199
+ `[${PLUGIN_NAME}] Failed to transform "${filePath}": ${error.message}`,
200
+ );
201
+ } else {
202
+ throw error;
203
+ }
204
+ }),
205
+ );
206
+ }
207
+ await Promise.all(transforms);
208
+
209
+ // Collect rules from each map
210
+ const libraryRules = Array.from(libraryRulesMap.values()).flat();
211
+ const productRules = Array.from(productRulesMap.values()).flat();
212
+
213
+ // Process each group separately
214
+ const libraryCss = libraryRules.length
215
+ ? stylexBabelPlugin.processStylexRules(libraryRules, {
216
+ useLayers: true,
217
+ })
218
+ : '';
219
+ const productCss = productRules.length
220
+ ? stylexBabelPlugin.processStylexRules(productRules, {
221
+ useLayers: true,
222
+ })
223
+ : '';
224
+
225
+ // Wrap in named layers
226
+ const parts = [];
227
+ if (libraryCss) {
228
+ parts.push(`@layer ${layers.library} {\n${libraryCss}\n}`);
229
+ }
230
+ if (productCss) {
231
+ parts.push(`@layer ${layers.product} {\n${productCss}\n}`);
232
+ }
233
+
234
+ const finalCss = parts.join('\n\n');
235
+ const parsed = await postcss.parse(finalCss, {from: fileName});
236
+ styleXAtRule.replaceWith(parsed);
237
+ result.root = root;
238
+
239
+ if (!shouldSkipTransformError) {
240
+ shouldSkipTransformError = true;
241
+ }
242
+ },
243
+ ],
244
+ };
245
+ };
246
+
247
+ plugin.postcss = true;
248
+ return plugin;
249
+ }
250
+
251
+ module.exports = createPlugin();
package/src/next.js ADDED
@@ -0,0 +1,72 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ "use strict";
4
+
5
+ /**
6
+ * @pharos-ds/build/next
7
+ *
8
+ * Next.js configuration helper for Pharos source builds.
9
+ *
10
+ * Usage in next.config.mjs:
11
+ * import {withPharos} from '@pharos-ds/build/next';
12
+ * export default withPharos({
13
+ * // your normal next config
14
+ * });
15
+ */
16
+
17
+ /**
18
+ * Wraps a Next.js config to enable Pharos source builds.
19
+ * - Adds transpilePackages for @pharos-ds/* packages
20
+ * - Sets conditionNames to resolve source exports
21
+ */
22
+ function withPharos(nextConfig = {}) {
23
+ const pharosPackages = [
24
+ '@pharos-ds/core',
25
+ '@pharos-ds/theme-neutral',
26
+ '@pharos-ds/lab',
27
+ ];
28
+
29
+ const existingTranspile = nextConfig.transpilePackages || [];
30
+ const merged = Array.from(new Set([...existingTranspile, ...pharosPackages]));
31
+
32
+ const existingWebpack = nextConfig.webpack;
33
+
34
+ return {
35
+ ...nextConfig,
36
+ transpilePackages: merged,
37
+ webpack: (config, context) => {
38
+ // Pharos packages are consumed from their `source` export (raw TS) so
39
+ // the sandbox/docsite build straight from src without a prebuild step.
40
+ // Apply the `source` condition via a scoped, ALLOWLIST rule that only
41
+ // matches @pharos-ds packages — the global conditions stay as Next's
42
+ // defaults (webpack's `'...'` sentinel + react-server), which React JSX
43
+ // resolution depends on. Third-party deps (e.g. `lexical`, which also
44
+ // ships a `source` export) therefore resolve to their built output, not
45
+ // raw TS. This is robust to new third-party `source`-shipping deps.
46
+ config.module = config.module || {};
47
+ config.module.rules = config.module.rules || [];
48
+ config.module.rules.unshift({
49
+ test: /[\\/]node_modules[\\/]@pharos-ds[\\/]/,
50
+ resolve: {
51
+ conditionNames: ['source', '...'],
52
+ },
53
+ });
54
+
55
+ // Preserve the symlinked node_modules path so Next.js's
56
+ // transpilePackages matcher recognizes @pharos-ds/* packages under
57
+ // pnpm's symlinked layout. Without this, webpack dereferences
58
+ // the symlink to packages/<name>/... which doesn't contain
59
+ // "node_modules/@pharos-ds" and transpilation is silently skipped,
60
+ // breaking subpath imports like '@pharos-ds/core/AlertDialog'.
61
+ config.resolve.symlinks = false;
62
+
63
+ // Call user's webpack config if provided
64
+ if (existingWebpack) {
65
+ return existingWebpack(config, context);
66
+ }
67
+ return config;
68
+ },
69
+ };
70
+ }
71
+
72
+ module.exports = {withPharos};