Skip to content

Commit

Permalink
handle external CSS
Browse files Browse the repository at this point in the history
  • Loading branch information
mrm007 committed Jul 26, 2023
1 parent 8f7ec09 commit 41afa21
Show file tree
Hide file tree
Showing 23 changed files with 279 additions and 86 deletions.
26 changes: 26 additions & 0 deletions .changeset/css-exports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
'@crackle/core': minor
---

Crackle now supports bundling external CSS.

This is useful when consuming packages which come with their own CSS, such as [Pure React Carousel](https://github.com/express-labs/pure-react-carousel).

External CSS can be imported with a CSS `@import` rule, similar to how you would import a JavaScript or TypeScript module:

```css
/* src/components/MyComponent/third-party.css */
@import 'package-with-styles/dist/styles.css';
```

```tsx
// src/components/MyComponent.tsx
import './third-party.css';

export const MyComponent = () => {
// ...
};
```

Crackle will bundle all external CSS into one file and output it to the `dist` directory.
Package exports will also be updated so consumers can import the bundled CSS.
4 changes: 4 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ fixtures/with-side-effects/dist
fixtures/with-side-effects/reset
# end managed by crackle

# managed by crackle
fixtures/with-styles/dist
# end managed by crackle

# managed by crackle
fixtures/with-vocab/dist
# end managed by crackle
Expand Down
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ fixtures/with-side-effects/dist
fixtures/with-side-effects/reset
# end managed by crackle

# managed by crackle
fixtures/with-styles/dist
# end managed by crackle

# managed by crackle
fixtures/with-vocab/dist
# end managed by crackle
Expand Down
3 changes: 3 additions & 0 deletions fixtures/with-styles/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# managed by crackle
/dist
# end managed by crackle
3 changes: 3 additions & 0 deletions fixtures/with-styles/package-with-styles/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "@sku-fixtures/package-with-styles"
}
4 changes: 4 additions & 0 deletions fixtures/with-styles/package-with-styles/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.external {
display: none;
font-size: 9px;
}
36 changes: 36 additions & 0 deletions fixtures/with-styles/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "@crackle-fixtures/with-styles",
"version": "1.0.0",
"private": true,
"license": "MIT",
"author": "SEEK",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./dist/style.css": "./dist/style.css",
"./package.json": "./package.json"
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist",
"init"
],
"scripts": {
"dev": "crackle dev",
"fix": "crackle fix",
"package": "crackle package"
},
"dependencies": {
"@crackle-fixtures/package-with-styles": "link:./package-with-styles",
"@vanilla-extract/css": "^1.12.0",
"react": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.0.21"
}
}
5 changes: 5 additions & 0 deletions fixtures/with-styles/src/Component.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { style } from '@vanilla-extract/css';

export const redBorder = style({
border: '5px solid red',
});
9 changes: 9 additions & 0 deletions fixtures/with-styles/src/Component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import './thirdparty.css';

import * as styles from './Component.css';

export default () => (
<div className={styles.redBorder}>
<span className="external" />
</div>
);
1 change: 1 addition & 0 deletions fixtures/with-styles/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Component } from './Component';
1 change: 1 addition & 0 deletions fixtures/with-styles/src/thirdparty.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import '@crackle-fixtures/package-with-styles/styles.css';
21 changes: 18 additions & 3 deletions packages/core/src/entries/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import fs from 'fs/promises';
import path from 'path';

import chalk from 'chalk';
import type { RollupOutput } from 'rollup';

import { type EnhancedConfig, type PartialConfig, getConfig } from '../config';
import { distDir } from '../constants';
import { createBundle } from '../package-utils/bundle';
import { createDtsBundle } from '../package-utils/dts';
import { renderPackageJsonValidationError } from '../reporters/package';
Expand All @@ -15,7 +17,10 @@ import {
getPackageEntryPoints,
} from '../utils/entry-points';
import { updateGitignore } from '../utils/gitignore';
import { validatePackageJson } from '../utils/setup-package-json';
import {
updatePackageJsonExports,
validatePackageJson,
} from '../utils/setup-package-json';

import { fix } from './fix';
import { logger } from './logger';
Expand Down Expand Up @@ -62,11 +67,12 @@ const build = async (config: EnhancedConfig, packageName: string) => {
label: string,
) => {
logger.info(`⚙️ Creating ${chalk.bold(label)} bundle...`);
await bundle(config, entries);
const result = await bundle(config, entries);
logger.info(`⚙️ Finished creating ${chalk.bold(label)} bundle`);
return result;
};

await Promise.all([
const [bundles] = await Promise.all([
withLogging(createBundle, 'esm/cjs'),
withLogging(createDtsBundle, 'dts'),
]);
Expand All @@ -75,6 +81,15 @@ const build = async (config: EnhancedConfig, packageName: string) => {

await updateGitignore(config.root, entries);

const cssExports = (bundles as RollupOutput[])
.map((bundle) => bundle.output)
.flat()
.map((output) => output.fileName)
.filter((fileName) => fileName.endsWith('.css'))
.map((fileName) => path.join(distDir, fileName));

await updatePackageJsonExports(config.root, cssExports);

logger.info(`✅ Successfully built ${chalk.bold.green(packageName)}!`);
};

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/package-utils/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export const createBundle = async (
} satisfies OutputOptions;
};

await viteBuild({
return await viteBuild({
...commonViteConfig,
esbuild: {
jsx: 'automatic',
Expand Down
7 changes: 5 additions & 2 deletions packages/core/src/package-utils/dts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ export const createDtsBundle = async (
const bundle = await rollup({
input: entries.map((entry) => entry.entryPath),
plugins: [
// patching imports is not needed for dts, as TypeScript can handle it (for now)
externals(config, 'cjs'),
externals(
config,
'cjs', // patching imports is not needed for dts, as TypeScript can handle it (for now)
/\.css$/, // ignore CSS files
),
dts({
respectExternal: true,
compilerOptions: config.dtsOptions as any,
Expand Down
26 changes: 22 additions & 4 deletions packages/core/src/plugins/rollup/externals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,11 @@ async function findDependencies(options: ExternalsOptions) {
return packagesById;
}

export function externals(config: EnhancedConfig, format?: Format): Plugin {
export function externals(
config: EnhancedConfig,
format?: Format,
forceExternal?: RegExp,
): Plugin {
const packageRoot = config.root;
const packagePath = path.join(packageRoot, 'package.json');
// eslint-disable-next-line no-sync
Expand Down Expand Up @@ -138,12 +142,12 @@ export function externals(config: EnhancedConfig, format?: Format): Plugin {

resolveId: {
order: 'pre',
async handler(id, ...rest) {
// `resolveId` is async in Rollup 3
async handler(id, importer, hookOptions) {
const resolved = await (plugin as FunctionPluginHooks).resolveId.call(
this,
id,
...rest,
importer,
hookOptions,
);

if (
Expand All @@ -166,6 +170,20 @@ export function externals(config: EnhancedConfig, format?: Format): Plugin {

return patched;
}

if (forceExternal) {
const resolvedByRollup = await this.resolve(id, importer, {
skipSelf: true,
...hookOptions,
});
if (resolvedByRollup && forceExternal.test(resolvedByRollup.id)) {
return {
id: resolvedByRollup.id,
external: true,
};
}
}

if (!id.match(ABSOLUTE_OR_RELATIVE)) {
logDebugOnce(`Internalized dependency ${id}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,37 +98,6 @@ Snapshot Diff:
"main": "./dist/index.cjs",
`;
exports[`diffPackageJson > incorrect package.json > exports out of order > diffs 1`] = `
[
{
"key": "exports",
},
]
`;
exports[`diffPackageJson > incorrect package.json > exports out of order > package.json 1`] = `
Snapshot Diff:
- Diff A
+ Diff B
@@ --- --- @@
"exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.mjs",
+ "require": "./dist/index.cjs",
+ },
"./css": {
@@ --- --- @@
"./package.json": "./package.json",
- ".": {
- "types": "./dist/index.d.ts",
- "import": "./dist/index.mjs",
- "require": "./dist/index.cjs",
- },
},
`;
exports[`diffPackageJson > incorrect package.json > files missing > diffs 1`] = `
[
{
Expand Down
17 changes: 0 additions & 17 deletions packages/core/src/utils/setup-package-json.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,23 +158,6 @@ describe('diffPackageJson', () => {
expectSnapshots({ diffs, packageJson, expectedPackageJson });
});

test('exports out of order', () => {
const packageJson = structuredClone(correctPackageJson);
const { '.': index, ...otherExports } = packageJson.exports;
packageJson.exports = {
...otherExports,
'.': index,
};

const { diffs, expectedPackageJson } = diffPackageJson(
packageRoot,
packageJson,
entries,
);

expectSnapshots({ diffs, packageJson, expectedPackageJson });
});

test('files missing', () => {
const packageJson = structuredClone(correctPackageJson);
// @ts-ignore
Expand Down
Loading

0 comments on commit 41afa21

Please sign in to comment.