13

Goals

  1. I want to display some code for reference from the project itself.
  2. I want the display of the code to be updated with the implementation.
  3. I don't want to eject from create-react-app

This react project, created with create-react-app and typescript, is going to be used to display some custom components for re-use in other projects. My goal is to have the component be used right next to the code that is using it.

How can I load the file if I don't have access to the webpack config, and I can't use fs.readFile?

counterbeing
  • 2,721
  • 2
  • 27
  • 47

2 Answers2

19

I managed to get this working after a bit of looking around. There are two major pieces that had to be in place to make it work.

Use the appropriate loader

In this case I wanted to use the raw-loader, so I installed it as a dev dependency. yarn add -D raw-loader.

In order to actually import the file I needed to then override the webpack configuration like this:

// eslint-disable-next-line import/no-webpack-loader-syntax
import toolbarItems from '!!raw-loader!../ToolbarItems';

This loads the entire file into the variable toolbarItems. By using the !! before the loader I prevent any other webpack loaders from processing it in this specific case. This might work on its own in a plain javascript project, but in typescript...

You must provide a module to typescript

I was running into the typescript error:

Failed to compile.

/Users/cory/Code/something/custom-theme/src/pages/NavigationDemo.tsx
TypeScript error in /Users/cory/Code/something/custom-theme/src/pages/NavigationDemo.tsx(9,26):
Cannot find module '!!raw-loader!../ToolbarItems'.  TS2307

     7 |
     8 | // eslint-disable-next-line import/no-webpack-loader-syntax
  >  9 | import toolbarItems from '!!raw-loader!../ToolbarItems';
       |                          ^
    10 |
    11 | const useStyles = makeStyles({
    12 |   root: {

Simply declaring a module for the loader in a file called ToolbarItems.ts.d.ts solved the issue for me:

declare module '!!raw-loader!*' {
  const content: string;
  export default content;
}

source

counterbeing
  • 2,721
  • 2
  • 27
  • 47
  • +1 yes, I do this too. But how to do you test the resulting react app with Jest? How can I teach Jest to understand the !!raw-loader! directive? – Larry K Aug 02 '20 at 21:51
  • Sorry, I don't have an answer for you there. But be sure to report back if you figure it out! – counterbeing Dec 17 '20 at 15:54
  • 1
    @counterbeing I believe you are missing an ! in the typing file. `!!raw-loader` – Pooya Jan 11 '22 at 14:19
  • Thanks for the catch @Pooya, updated. – counterbeing Jan 12 '22 at 03:09
  • 1
    Doesn't work for me with CRA 5.0.1 it changes the content of the file, but the imported string is still a url from which you have to fetch the file content. – Sasha Davydenko Oct 21 '22 at 12:51
  • 1
    @SashaDavydenko I've abandoned CRA for vite, but if you come up with a solution that works, I'm happy to put a header at the top of the current answer and upvote you :) – counterbeing Oct 22 '22 at 19:06
6

Since you use create-react-app for your project, the best solution at the moment would be a babel plugin called Raw.Macro.

This plugin allows you to access content of your files without a need to create-react-app eject. Provides really elegant solution without any boilerplate code and declaring "d.ts" files as in previous answer.

Note: There is a small drawback that you have to re-start your webpack dev server when the file changes, because the content of a file is being embedded during the build process.

import raw from 'raw.macro';

function foo(){
    const jsonContent = raw('../utils/stops.json');
    console.log(jsonContent);
}
Marcel Šerý
  • 156
  • 1
  • 6