CSS / SCSS-Module können nicht importiert werden. TypeScript sagt "Modul kann nicht gefunden werden"

82

Ich versuche, ein Thema aus einem CSS-Modul zu importieren, aber TypeScript gibt den Fehler "Modul kann nicht gefunden werden" aus und das Thema wird zur Laufzeit nicht angewendet. Ich denke, mit meiner Webpack-Konfiguration stimmt etwas nicht, aber ich bin mir nicht sicher, wo das Problem liegt.

Ich benutze die folgenden Tools:

"typescript": "^2.0.3"
"webpack": "2.1.0-beta.25"
"webpack-dev-server": "^2.1.0-beta.9"
"react": "^15.4.0-rc.4"
"react-toolbox": "^1.2.3"
"node-sass": "^3.10.1"
"style-loader": "^0.13.1"
"css-loader": "^0.25.0"
"sass-loader": "^4.0.2"
"sass-lint": "^1.9.1"
"sasslint-webpack-plugin": "^1.0.4"

Hier ist mein webpack.config.js

var path = require('path');
var webpack = require('webpack');
var sassLintPlugin = require('sasslint-webpack-plugin');

module.exports = {
  entry: [
    'webpack-dev-server/client?http://localhost:8080',
    'webpack/hot/dev-server',
    './src/index.tsx',
  ],
  output: {
    path: path.resolve(__dirname, 'dist'),
    publicPath: 'http://localhost:8080/',
    filename: 'dist/bundle.js',
  },
  devtool: 'source-map',
  resolve: {
    extensions: ['.webpack.js', '.web.js', '.ts', '.tsx', '.js'],
  },
  module: {
    rules: [{
      test: /\.js$/,
      loader: 'source-map-loader',
      exclude: /node_modules/,
      enforce: 'pre',
    }, {
      test: /\.tsx?$/,
      loader: 'tslint-loader',
      exclude: /node_modules/,
      enforce: 'pre',
    }, {
      test: /\.tsx?$/,
      loaders: [
        'react-hot-loader/webpack',
        'awesome-typescript-loader',
      ],
      exclude: /node_modules/,
    }, {
      test: /\.scss$/,
      loaders: ['style', 'css', 'sass']
    }, {
      test: /\.css$/,
      loaders: ['style', 'css']
    }],
  },
  externals: {
    'react': 'React',
    'react-dom': 'ReactDOM'
  },
  plugins: [
    new sassLintPlugin({
      glob: 'src/**/*.s?(a|c)ss',
      ignoreFiles: ['src/normalize.scss'],
      failOnWarning: false, // Do it.
    }),
    new webpack.HotModuleReplacementPlugin(),
  ],
  devServer: {
    contentBase: './'
  },
};

und App.tsxwo ich versuche zu importieren:

import * as React from 'react';

import { AppBar } from 'react-toolbox';
import appBarTheme from 'react-toolbox/components/app_bar/theme.scss'
// local ./theme.scss stylesheets aren't found either 

interface IAppStateProps {
  // No props yet
}

interface IAppDispatchProps {
  // No state yet
}

class App extends React.Component<IAppStateProps & IAppDispatchProps, any> {

  constructor(props: IAppStateProps & IAppDispatchProps) {
    super(props);
  }

  public render() {
    return (

        <div className='wrapper'>
          <AppBar title='My App Bar' theme={appBarTheme}>
          </AppBar>
        </div>

    );
  }
}

export default App;

Was ist noch erforderlich, um den Import von typsicheren Stylesheet-Modulen zu ermöglichen?

Zakdances
quelle

Antworten:

120

TypeScript weiß nicht, dass es andere Dateien als .tsoder .tsxso gibt, und es wird ein Fehler ausgegeben, wenn ein Import ein unbekanntes Dateisuffix hat.

Wenn Sie über eine Webpack-Konfiguration verfügen, mit der Sie andere Dateitypen importieren können, müssen Sie dem TypeScript-Compiler mitteilen, dass diese Dateien vorhanden sind. Fügen Sie dazu eine Deklarationsdatei hinzu, in der Sie Module mit passenden Namen deklarieren.

Der Inhalt des zu deklarierenden Moduls hängt vom für den Dateityp verwendeten Webpack-Loader ab. In einer Webpack-Konfiguration, die *.scssDateien über Sass-LoaderCSS-LoaderStyle-Loader weiterleitet , enthält das importierte Modul keinen Inhalt, und die richtige Moduldeklaration sieht folgendermaßen aus:

// declaration.d.ts
declare module '*.scss';

Wenn die Loader für CSS-Module konfiguriert sind, erweitern Sie die Deklaration einfach wie folgt:

// declaration.d.ts
declare module '*.scss' {
    const content: {[className: string]: string};
    export default content;
}
Kalle
quelle
1
Hey das hat bei mir nicht geklappt, hat sich in letzter Zeit etwas geändert. stackoverflow.com/questions/56563243/…
Kay
45

Hier ist eine vollständige Konfiguration, die für mich funktioniert (ich habe gerade eine Stunde lang schmerzhaft versucht - falls jemand auf die gleichen Probleme stößt):

TypeScript + WebPack + Sass

webpack.config.js

module.exports = {
  //mode: "production", 
    mode: "development", devtool: "inline-source-map",

    entry: [ "./src/app.tsx"/*main*/ ], 
    output: {
        filename: "./bundle.js"  // in /dist
    },
    resolve: {
        // Add `.ts` and `.tsx` as a resolvable extension.
        extensions: [".ts", ".tsx", ".js", ".css", ".scss"]
    },
    module: {
        rules: [

            { test: /\.tsx?$/, loader: "ts-loader" }, 

            { test: /\.scss$/, use: [ 
                { loader: "style-loader" },  // to inject the result into the DOM as a style block
                { loader: "css-modules-typescript-loader"},  // to generate a .d.ts module next to the .scss file (also requires a declaration.d.ts with "declare modules '*.scss';" in it to tell TypeScript that "import styles from './styles.scss';" means to load the module "./styles.scss.d.td")
                { loader: "css-loader", options: { modules: true } },  // to convert the resulting CSS to Javascript to be bundled (modules:true to rename CSS classes in output to cryptic identifiers, except if wrapped in a :global(...) pseudo class)
                { loader: "sass-loader" },  // to convert SASS to CSS
                // NOTE: The first build after adding/removing/renaming CSS classes fails, since the newly generated .d.ts typescript module is picked up only later
            ] }, 

        ]
    }
}; 

Fügen Sie auch ein declarations.d.tsin Ihr Projekt ein:

// We need to tell TypeScript that when we write "import styles from './styles.scss' we mean to load a module (to look for a './styles.scss.d.ts'). 
declare module '*.scss'; 

Und all dies benötigen Sie in Ihren package.jsonEntwicklungsabhängigkeiten:

  "devDependencies": {
    "@types/node-sass": "^4.11.0",
    "node-sass": "^4.12.0",
    "css-loader": "^1.0.0",
    "css-modules-typescript-loader": "^2.0.1",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "ts-loader": "^5.3.3",
    "typescript": "^3.4.4",
    "webpack": "^4.30.0",
    "webpack-cli": "^3.3.0"
  }

Dann sollten Sie eine mystyle.d.tsneben Ihrer mystyle.scssListe mit den von Ihnen definierten CSS-Klassen erhalten, die Sie als Typescript-Modul importieren und wie folgt verwenden können:

import * as styles from './mystyles.scss'; 

const foo = <div className={styles.myClass}>FOO</div>; 

Das CSS wird automatisch geladen (als styleElement in das DOM eingefügt) und enthält kryptische Bezeichner anstelle Ihrer CSS-Klassen in der .scss-Datei, um Ihre Stile auf der Seite zu isolieren (sofern Sie dies nicht verwenden):global(.a-global-class) { ... } ).

Beachten Sie, dass die erste Kompilierung fehlschlägt, wenn Sie CSS-Klassen hinzufügen oder entfernen oder umbenennen, da die importierte mystyles.d.ts die alte Version ist und nicht die neue Version, die gerade während der Kompilierung generiert wurde. Einfach nochmal kompilieren.

Genießen.

frevd
quelle
"HINWEIS: Der erste Build nach dem Hinzufügen / Entfernen / Umbenennen von CSS-Klassen schlägt fehl, da das neu generierte .d.ts-Typoskriptmodul erst später abgerufen wird." - Wie wird dieser Teil gelöst?
Jon Lauridsen
3
@ JonLauridsen kann gelöst werden, indem ts-loader am unteren Ende des Arrays der Regeln in webpack.config.js festgelegt wird. Ts-loader nimmt also bei jeder Kompilierung eine korrekte * .scss.d.ts-Datei auf, da diese zuvor generiert wurde.
Yan Pak
1
@YanPak Danke, das Hinzufügen eines global.d.tszu meinem srcVerzeichnis zusammen mit dem Verschieben des ts- Loaders unter die Style-Loader hat es für mich behoben.
Lux
4

Heads Up, wenn Sie die Pfadeinstellung tsconfig.json verwenden

Beachten Sie, dass Sie zusätzliche Konfiguration benötigen, wenn Sie diese Lösungen in Verbindung mit tsconfig-Pfaden verwenden, um Ihren Import zu verkürzen.

Wenn Sie zufällig einen Pfad wie folgt verwenden:

{
  "compilerOptions": {
    "paths": {
      "style/*": [ "src/style/*" ],
    }
  }
}

So können Sie tun:

import { header } from 'style/ui.scss';

Dann müssen Sie auch ein Modul hinzufügen , um die Konfiguration in Ihrem Webpack wie folgt aufzulösen:

module.exports = {
  ...
  resolve: {
    ...
    alias: {
      style: path.resolve(__dirname, 'src', 'style')
    }
  }
}

Stellen Sie sicher, dass der Pfad gemäß Ihrem Setup festgelegt ist.

Dadurch weiß das Webpack, wo es suchen muss, da es glaubt, dass der neue Importpfad tatsächlich ein Modul ist, und standardmäßig das Verzeichnis node_modules verwendet. Mit dieser Konfiguration weiß es, wo es suchen und finden muss, und der Build funktioniert.

dbrody
quelle