Skip to content

Commit

Permalink
Generate TypeScript imports module (#1465)
Browse files Browse the repository at this point in the history
* Add TypeScript to userdeps templates

* Convert user deps import extensions to .js

* Update native.ts

* Make it optional to import JS extension in userdeps

* Make static imports more robust

* Update native.ts

* Add typeScript option

* Only generate TS userdeps on disk

* Refactor

* Clean up paths

* Refactor userdeps flag into new exposeModules option

* Specify native port in test workflow

* Improve ignore files

* Rename server plugin

* Improve user deps formatting

* Rename user deps to user imports

* s/exposeModules/exposeImports

* s/modules/imports

* Update exposeImports.ts

* Update .prettierignore

* Update reactNative.md

* Update exposeImports.ts

* Update exposeImports.ts

* Update types.ts

* Tweak stuff

* Rename user deps to user imports in webpack package

* Rename tests
  • Loading branch information
ovidiuch committed Apr 27, 2023
1 parent 41a6563 commit f8a9e85
Show file tree
Hide file tree
Showing 57 changed files with 885 additions and 581 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ jobs:
start: |
yarn workspace example-${{ matrix.example }} start --lazy=${{ matrix.lazy == true }},
yarn workspace example-${{ matrix.example }} serve,
yarn workspace example-${{ matrix.example }} native --lazy=${{ matrix.lazy == true }}
yarn workspace example-${{ matrix.example }} native --lazy=${{ matrix.lazy == true }} --port 5002
wait-on: 'http://localhost:5000,http://localhost:5001,http://localhost:5002'
env:
CYPRESS_EXAMPLE_NAME: ${{ matrix.example }}
Expand Down
9 changes: 4 additions & 5 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
.vscode
cosmos-export
cosmos.imports.js
cosmos.imports.ts
coverage
examples/*/cosmos-export
examples/*/cosmos.userdeps.js
examples/*/package.json
lerna.json
package.json
packages/*/dist
packages/*/package.json
website/dist
testMocks
website/dist
26 changes: 13 additions & 13 deletions cypress/tests/native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,40 +32,40 @@ describe('Native', () => {
});
});

context('user deps file', () => {
context('imports file', () => {
it('has port option', () => {
getUserDepsFile().should(
getImportsFile().should(
'contain',
`"playgroundUrl": "http://localhost:5002"`
);
});

it('has fixture paths', () => {
containsImport('src/__fixtures__/HelloWorld.ts');
containsImport('src/Counter.fixture.tsx');
containsImport('src/WelcomeMessage/WelcomeMessage.fixture.tsx');
containsImport('src/__fixtures__/HelloWorld.js');
containsImport('src/Counter.fixture.js');
containsImport('src/WelcomeMessage/WelcomeMessage.fixture.js');
});

it('has decorator paths', () => {
containsImport('src/WelcomeMessage/cosmos.decorator.tsx');
containsImport('src/WelcomeMessage/cosmos.decorator.js');
});
});
});

function getUserDepsFile() {
return cy.readFile(`examples/${exampleName()}/cosmos.userdeps.js`);
function getImportsFile() {
return cy.readFile(`examples/${exampleName()}/cosmos.imports.ts`);
}

function containsImport(modulePath: string) {
function containsImport(importPath: string) {
if (lazy()) {
getUserDepsFile().should(
getImportsFile().should(
'match',
new RegExp(`import\\('./${modulePath}'\\)`)
new RegExp(`import\\('./${importPath}'\\)`)
);
} else {
getUserDepsFile().should(
getImportsFile().should(
'match',
new RegExp(`import [a-z0-9]+ from './${modulePath}'`)
new RegExp(`import \\* as [a-z0-9]+ from './${importPath}'`)
);
}
}
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ This is a `.babelrc` example for Next.js:

#### Fixtures not detected?

- Run `cosmos` with the `--external-userdeps` flag. This should generate `cosmos.userdeps.js`. Check that file to see if your fixtures are being picked up by Cosmos.
- Run `cosmos` with the `--expose-imports` flag. This should generate `cosmos.imports.js`. Check that file to see if your fixtures are being picked up by Cosmos.
- Check your directory structure. If you are using a Cosmos config file, Cosmos will use the directory of the config file as the root of your project. If your Cosmos config file is nested in a directory that isn't an ancestor of your fixture files, Cosmos will not find your fixtures. To solve this add a [`rootDir`](https://github.com/react-cosmos/react-cosmos/blob/d800a31b39d82c810f37a2ad0d25eed5308b830a/packages/react-cosmos/config.schema.json#L10-L14) entry to your Cosmos config pointing to your root directory.

---
Expand Down
6 changes: 3 additions & 3 deletions docs/customBundlerSetup.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,18 @@ Next, choose a port for the renderer other than the main Cosmos port, say `5050`
}
```

Next, start Cosmos with the `--external-userdeps` CLI flag. This will generate a `cosmos.userdeps.js` module that contains maps of user module imports (fixtures and decorators) as well the renderer config. Feel free to add this file to .gitignore.
Next, start Cosmos with the `--expose-imports` CLI flag. This will generate a `cosmos.imports.js` module that contains maps of user module imports (fixtures and decorators) as well the renderer config. Feel free to add this file to .gitignore.

Finally, create a web server using your bundler of choice that serves an `index.html`, which loads a JS module with the following code:

```js
import { mountDomRenderer } from 'react-cosmos-dom';
import * as mountArgs from './cosmos.userdeps.js';
import * as mountArgs from './cosmos.imports.js';

mountDomRenderer(mountArgs);
```

That's it, really. The Cosmos Server will automatically update `cosmos.userdeps.js` when fixture/decorator files are added, changed or removed.
That's it, really. The Cosmos Server will automatically update `cosmos.imports.js` when fixture/decorator files are added, changed or removed.

To learn how turn your setup into a Cosmos plugin check out the [Vite plugin](../packages/react-cosmos-plugin-vite).

Expand Down
20 changes: 6 additions & 14 deletions docs/reactNative.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ yarn cosmos

🚀 Open **[localhost:5000](http://localhost:5000)** in your browser.

> You'll notice Cosmos generated a `cosmos.userdeps.js` module, which becomes relevant in step 5. You can add this file to .gitignore.
> You'll notice Cosmos generated a `cosmos.imports.js` module, which becomes relevant in step 5. You can add this file to .gitignore.
The `Hello` fixture will show up in your React Cosmos UI.

Expand Down Expand Up @@ -110,7 +110,7 @@ Then add the Cosmos renderer under `App.cosmos.js`:
// App.cosmos.js
import React, { Component } from 'react';
import { NativeFixtureLoader } from 'react-cosmos-native';
import { rendererConfig, moduleWrappers } from './cosmos.userdeps.js';
import { rendererConfig, moduleWrappers } from './cosmos.imports.js';

export default class CosmosApp extends Component {
render() {
Expand All @@ -124,16 +124,6 @@ export default class CosmosApp extends Component {
}
```

> When using TypeScript you'll notice an error related to `cosmos.userdeps.js`, which is a plain JS module. We're working on providing an option to generate `cosmos.userdeps.ts` soon. Meanwhile you can ignore this error by slapping a naughty `@ts-ignore` comment:
>
> ```diff
> <NativeFixtureLoader
> rendererConfig={rendererConfig}
> + // @ts-ignore
> moduleWrappers={moduleWrappers}
> />
> ```
Finally, create a new `App.js` that routes between your main and Cosmos entry points based on enviromnent:

```js
Expand Down Expand Up @@ -170,10 +160,12 @@ You can configure the Cosmos Native renderer to auto load a fixture on init.
<NativeFixtureLoader
rendererConfig={rendererConfig}
moduleWrappers={moduleWrappers}
+ initialFixtureId={{ path: 'Hello.fixture.js' }}
+ initialFixtureId={{ path: 'src/__fixtures__/HelloWorld.ts' }}
/>
```

`initialFixtureId` expects a fixture path relative to the project root. You'll find the exact path in `cosmos.imports.js` as a key in the `fixtures` object.

## Troubleshooting

- React Native blacklists `__fixtures__` dirs by default (at least it used to). Unless you configure Cosmos to use a different directory pattern, you need to [override `getBlacklistRE` in the React Native CLI config](https://github.com/skidding/jobs-done/blob/585b1c472a123c9221dfec9018c9fa1e976d715e/rn-cli.config.js).
Expand All @@ -184,6 +176,6 @@ You can get Cosmos to [mirror your fixtures on both DOM and Native renderers](ht

1. Set up Cosmos for Native using the steps above.
2. Set up the react-cosmos-webpack-plugin as described [here](README.md#getting-started).
3. Start Cosmos with the `cosmos --external-userdeps` command.
3. Start Cosmos with the `cosmos --expose-imports` command.

[Join us on Discord](https://discord.gg/3X95VgfnW5) for feedback, questions and ideas.
2 changes: 1 addition & 1 deletion examples/vite/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
cosmos-export
cosmos.userdeps.js
cosmos.imports.ts
2 changes: 1 addition & 1 deletion examples/vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"start": "cosmos",
"export": "cosmos-export",
"serve": "http-server -p 5001 ./cosmos-export",
"native": "cosmos-native --port 5002"
"native": "cosmos-native"
},
"dependencies": {
"@vitejs/plugin-react": "^4.0.0",
Expand Down
2 changes: 1 addition & 1 deletion examples/webpack/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
cosmos-export
cosmos.userdeps.js
cosmos.imports.ts
2 changes: 1 addition & 1 deletion examples/webpack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"start": "cosmos",
"export": "cosmos-export",
"serve": "http-server -p 5001 ./cosmos-export",
"native": "cosmos-native --port 5002"
"native": "cosmos-native"
},
"dependencies": {
"@babel/core": "^7.21.4",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
export function createViteRendererIndex(userDepsModuleId: string) {
export function createViteRendererIndex(userImportsModuleId: string) {
return `
import { mountDomRenderer } from 'react-cosmos-dom';
mount();
async function mount() {
// Use dynamic import to reload updated modules upon hot reloading
const args = await import('${userDepsModuleId}');
const args = await import('${userImportsModuleId}');
mountDomRenderer(args);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import path from 'node:path';
import {
CosmosConfig,
generateUserDepsModule,
generateUserImports,
getPlaygroundUrl,
} from 'react-cosmos';
import { Plugin } from 'rollup';
import { CosmosViteConfig } from './createCosmosViteConfig.js';
import { createViteRendererIndex } from './createViteRendererIndex.js';

export const userDepsVirtualModuleId = 'virtual:cosmos-userdeps';
export const userDepsResolvedModuleId = '\0' + userDepsVirtualModuleId;
export const userImportsVirtualModuleId = 'virtual:cosmos-imports';
export const userImportsResolvedModuleId = '\0' + userImportsVirtualModuleId;

const defaultIndexPattern = new RegExp(
`^(src\\${path.sep})?(index|main)\.(js|ts)x?$`
Expand All @@ -23,22 +23,23 @@ export function reactCosmosViteRollupPlugin(
name: 'react-cosmos-vite-renderer',

resolveId(id) {
if (id === userDepsVirtualModuleId) {
return userDepsResolvedModuleId;
if (id === userImportsVirtualModuleId) {
return userImportsResolvedModuleId;
} else {
return null;
}
},

load(id: string) {
if (id == userDepsResolvedModuleId) {
return generateUserDepsModule({
if (id == userImportsResolvedModuleId) {
return generateUserImports({
cosmosConfig,
rendererConfig: {
playgroundUrl: getPlaygroundUrl(cosmosConfig),
containerQuerySelector: cosmosConfig.dom.containerQuerySelector,
},
relativeToDir: null,
typeScript: false,
});
} else {
return null;
Expand All @@ -55,7 +56,7 @@ export function reactCosmosViteRollupPlugin(
console.log(`[Cosmos] Replacing vite index module at ${relPath}`);

return {
code: createViteRendererIndex(userDepsVirtualModuleId),
code: createViteRendererIndex(userImportsVirtualModuleId),
map: null,
};
} else {
Expand Down
8 changes: 5 additions & 3 deletions packages/react-cosmos-plugin-vite/src/viteDevServerPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createServer } from 'vite';
import { createCosmosViteConfig } from './createCosmosViteConfig.js';
import {
reactCosmosViteRollupPlugin,
userDepsResolvedModuleId,
userImportsResolvedModuleId,
} from './reactCosmosViteRollupPlugin.js';

export async function viteDevServerPlugin({
Expand Down Expand Up @@ -37,10 +37,12 @@ export async function viteDevServerPlugin({
await server.listen();

const watcher = await startFixtureWatcher(cosmosConfig, 'add', () => {
const module = server.moduleGraph.getModuleById(userDepsResolvedModuleId);
const module = server.moduleGraph.getModuleById(
userImportsResolvedModuleId
);
if (!module) {
throw new Error(
`Vite module graph doesn't contain module with id ${userDepsResolvedModuleId}`
`Vite module graph doesn't contain module with id ${userImportsResolvedModuleId}`
);
}
server.moduleGraph.invalidateModule(module);
Expand Down
4 changes: 2 additions & 2 deletions packages/react-cosmos-plugin-webpack/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ mount();

async function mount() {
// Use dynamic import to reload updated modules upon hot reloading
const { rendererConfig, moduleWrappers } = await import('./userDeps.js');
const { rendererConfig, moduleWrappers } = await import('./userImports.js');
mountDomRenderer({
rendererConfig,
moduleWrappers,
Expand All @@ -14,7 +14,7 @@ async function mount() {
}

if ((import.meta as any).webpackHot) {
(import.meta as any).webpackHot.accept('./userDeps.js', () => {
(import.meta as any).webpackHot.accept('./userImports.js', () => {
// If a previous error has been solved, the error overlay auto-closes nicely.
// If the error persists, however, the overlay will pop up again on its own
dismissErrorOverlay();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
jest.mock('../webpackConfig/resolveWebpackLoaderPath.js', () => {
function resolveWebpackLoaderPath() {
return require.resolve('../webpackConfig/userDepsLoader.cjs');
return require.resolve('../webpackConfig/userImportsLoader.cjs');
}

return { resolveWebpackLoaderPath };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,11 @@ it('create output', async () => {
);
});

it('includes user deps loader', async () => {
it('includes user imports loader', async () => {
const { module } = await getCustomDevWebpackConfig();
expect(module!.rules).toContainEqual({
loader: require.resolve('../userDepsLoader'),
include: require.resolve('../../../client/userDeps'),
loader: require.resolve('../userImportsLoader'),
include: require.resolve('../../../client/userImports'),
options: { cosmosConfig },
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,11 @@ it('create output', async () => {
);
});

it('includes user deps loader', async () => {
it('includes user imports loader', async () => {
const { module } = await getCustomExportWebpackConfig();
expect(module!.rules).toContainEqual({
loader: require.resolve('../userDepsLoader'),
include: require.resolve('../../../client/userDeps'),
loader: require.resolve('../userImportsLoader'),
include: require.resolve('../../../client/userImports'),
options: { cosmosConfig },
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ it('create output', async () => {
);
});

it('includes user deps loader', async () => {
it('includes user imports loader', async () => {
const { module } = await getDefaultDevWebpackConfig();
expect(module!.rules).toContainEqual({
loader: require.resolve('../userDepsLoader'),
include: require.resolve('../../../client/userDeps'),
loader: require.resolve('../userImportsLoader'),
include: require.resolve('../../../client/userImports'),
options: { cosmosConfig },
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ it('create output', async () => {
);
});

it('includes user deps loader', async () => {
it('includes user imports loader', async () => {
const { module } = await getDefaultExportWebpackConfig();
expect(module!.rules).toContainEqual({
loader: require.resolve('../userDepsLoader'),
include: require.resolve('../../../client/userDeps'),
loader: require.resolve('../userImportsLoader'),
include: require.resolve('../../../client/userImports'),
options: { cosmosConfig },
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ function getRules(
{ module }: webpack.Configuration
) {
const existingRules = (module && module.rules) || [];
return [...existingRules, getUserDepsLoaderRule(cosmosConfig)];
return [...existingRules, getUserImportsLoaderRule(cosmosConfig)];
}

function getUserDepsLoaderRule(
function getUserImportsLoaderRule(
cosmosConfig: CosmosConfig
): webpack.RuleSetRule {
return {
loader: resolveWebpackLoaderPath(),
include: resolveWebpackClientPath('userDeps'),
include: resolveWebpackClientPath('userImports'),
options: { cosmosConfig },
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ import { createRequire } from 'node:module';

export function resolveWebpackLoaderPath() {
const require = createRequire(import.meta.url);
return require.resolve('./userDepsLoader.cjs');
return require.resolve('./userImportsLoader.cjs');
}

0 comments on commit f8a9e85

Please sign in to comment.