Nie można zaimportować modułów CSS / SCSS. TypeScript mówi „Cannot Find Module”

88

Próbuję zaimportować motyw z modułu CSS, ale TypeScript wyświetla błąd „Nie można znaleźć modułu”, a motyw nie jest stosowany w czasie wykonywania. Myślę, że coś jest nie tak z moją konfiguracją WebPacka, ale nie jestem pewien, gdzie jest problem.

Używam następujących narzędzi:

"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"

Tutaj jest mój 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: './'
  },
};

i mój, App.tsxgdzie próbuję zaimportować:

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;

Co jeszcze jest wymagane, aby umożliwić importowanie modułu arkusza stylów bezpiecznego?

zakdances
źródło

Odpowiedzi:

125

TypeScript nie wie, że istnieją pliki inne niż .tslub .tsxwięc, zgłosi błąd, jeśli import ma nieznany sufiks pliku.

Jeśli masz konfigurację pakietu internetowego, która umożliwia importowanie innych typów plików, musisz poinformować kompilator TypeScript, że te pliki istnieją. W tym celu dodaj plik deklaracji, w którym deklarujesz moduły z pasującymi nazwami.

Zawartość modułu do zadeklarowania zależy od programu ładującego pakiet webowy używanego dla typu pliku. W konfiguracji webpacka, która *.scssprzesyła pliki przez sass-loadercss-loaderstyle-loader , zaimportowany moduł nie będzie zawierał treści, a prawidłowa deklaracja modułu wyglądałaby następująco:

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

Jeśli moduły ładujące są skonfigurowane dla modułów css, po prostu rozszerz deklarację w następujący sposób:

// declaration.d.ts
declare module '*.scss' {
    const content: Record<string, string>;
    export default content;
}
Kalle
źródło
2
Hej, to mi się nie udało, ostatnio coś się zmieniło. stackoverflow.com/questions/56563243/...
Kay
50

Oto pełna konfiguracja, która działa dla mnie (właśnie spędziłem godzinę bolesnych prób i błędów - na wypadek, gdyby ktoś napotkał te same problemy):

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
            ] }, 

        ]
    }
}; 

declarations.d.tsUmieść również w swoim projekcie:

// 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'; 

I będziesz potrzebować tego wszystkiego w swoich package.jsonzależnościach od deweloperów:

  "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"
  }

Następnie powinieneś dostać mystyle.d.tsobok swojego mystyle.scsszawierającego klasy CSS, które zdefiniowałeś, które możesz zaimportować jako moduł Typescript i użyć w ten sposób:

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

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

CSS zostanie automatycznie załadowany (wstawiony jako styleelement do DOM) i będzie zawierał zaszyfrowane identyfikatory zamiast klas CSS w pliku .scss, aby wyodrębnić style na stronie (chyba że używasz :global(.a-global-class) { ... }).

Zauważ, że pierwsza kompilacja zakończy się niepowodzeniem za każdym razem, gdy dodasz klasy CSS, usuniesz je lub zmienisz ich nazwę, ponieważ zaimportowany plik mystyles.d.ts jest starą wersją, a nie nową, wygenerowaną właśnie podczas kompilacji. Po prostu skompiluj ponownie.

Cieszyć się.

frevd
źródło
„UWAGA: Pierwsza kompilacja po dodaniu / usunięciu / zmianie nazwy klas CSS kończy się niepowodzeniem, ponieważ nowo wygenerowany moduł maszynopisu .d.ts jest pobierany dopiero później” - Jak rozwiązać tę część?
Jon Lauridsen
3
@JonLauridsen można to rozwiązać, ustawiając ts-loader na dole (końcu) tablicy reguł w webpack.config.js. Więc ts-loader będzie pobierał poprawne pliki * .scss.d.ts podczas każdej kompilacji, ponieważ zostały one wcześniej wygenerowane.
Yan Pak
1
@YanPak Dzięki, dodanie a global.d.tsdo mojego srckatalogu wraz z przeniesieniem modułu ładującego ts poniżej modułów ładujących stylów naprawiło to za mnie.
lux
5

Uwaga, jeśli używasz ustawienia ścieżki tsconfig.json

Zwróć uwagę, że jeśli używasz tych rozwiązań w połączeniu ze ścieżkami tsconfig w celu skrócenia importu, będziesz potrzebować dodatkowej konfiguracji.

Jeśli zdarzy ci się użyć ścieżki tsconfig takiej jak:

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

Więc możesz zrobić:

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

Następnie musisz również dodać moduł rozwiązywania konfiguracji w swoim pakiecie internetowym, taki jak:

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

Upewnij się, że ścieżka jest ustawiona zgodnie z konfiguracją.

Dzięki temu webpack wie, gdzie szukać, ponieważ uważa, że ​​nowa ścieżka importu jest w rzeczywistości modułem, więc domyślnie jest to katalog node_modules. Dzięki tej konfiguracji wie, gdzie szukać i znajduje, a kompilacja działa.

dbrody
źródło
0

Używam modułów wtyczki-skryptu-css

npm install -D typescript-plugin-css-modules

tsconfig.json

{
  "compilerOptions": {
    "plugins": [{ "name": "typescript-plugin-css-modules" }]
  }
}

mugol
źródło