No se pueden importar módulos CSS / SCSS. TypeScript dice "No se puede encontrar el módulo"

82

Estoy tratando de importar un tema desde un módulo CSS pero TypeScript me da un error "No se puede encontrar el módulo" y el tema no se aplica en tiempo de ejecución. Creo que hay algún problema con la configuración de mi paquete web, pero no estoy seguro de dónde está el problema.

Estoy usando las siguientes herramientas:

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

Aquí está mi 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: './'
  },
};

y mi App.tsxdonde estoy tratando de importar:

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;

¿Qué más se requiere para habilitar la importación de módulos de hojas de estilo con seguridad de tipos?

zakdances
fuente

Respuestas:

120

TypeScript no sabe que hay archivos distintos de .tso, .tsxpor lo tanto, arrojará un error si una importación tiene un sufijo de archivo desconocido.

Si tiene una configuración de paquete web que le permite importar otros tipos de archivos, debe informar al compilador de TypeScript que estos archivos existen. Para hacerlo, agregue un archivo de declaración en el que declare módulos con nombres adecuados.

El contenido del módulo a declarar depende del cargador de paquete web utilizado para el tipo de archivo. En una configuración de paquete web que canaliza *.scssarchivos a través de sass-loadercss-loaderstyle-loader , no habrá contenido en el módulo importado y la declaración de módulo correcta se vería así:

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

Si los cargadores están configurados para módulos css, simplemente extienda la declaración de esta manera:

// declaration.d.ts
declare module '*.scss' {
    const content: {[className: string]: string};
    export default content;
}
Kalle
fuente
1
Oye, esto no funcionó para mí, algo ha cambiado recientemente. stackoverflow.com/questions/56563243/…
Kay
46

Aquí hay una configuración completa que funciona para mí (acabo de pasar una hora de doloroso ensayo y error en esto, en caso de que alguien tenga los mismos problemas):

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

        ]
    }
}; 

También ponga un declarations.d.tsen su proyecto:

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

Y necesitará todo esto en sus package.jsondependencias de desarrollo:

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

Luego, debe obtener un mystyle.d.tspróximo que mystyle.scsscontenga las clases CSS que definió, que puede importar como un módulo de TypeScript y usar así:

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

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

El CSS se cargará automáticamente (se inyectará como un styleelemento en el DOM) y contendrá identificadores crípticos en lugar de sus clases CSS en el .scss, para aislar sus estilos en la página (a menos que los use :global(.a-global-class) { ... }).

Tenga en cuenta que la primera compilación fallará siempre que agregue clases CSS o las elimine o les cambie el nombre, ya que mystyles.d.ts importado es la versión anterior y no la nueva versión que se acaba de generar durante la compilación. Simplemente compile de nuevo.

Disfrutar.

frevd
fuente
"NOTA: La primera compilación después de agregar / eliminar / cambiar el nombre de las clases CSS falla, ya que el módulo de mecanografiado .d.ts recién generado se recupera solo más tarde" - ¿Cómo resolver esa parte?
Jon Lauridsen
3
@JonLauridsen se puede resolver configurando ts-loader en la parte inferior (final) de la matriz de reglas en webpack.config.js. De modo que ts-loader recogerá los archivos * .scss.d.ts correctos en cada compilación porque se generarán antes.
Yan Pak
1
@YanPak Gracias, agregar un global.d.tsa mi srcdirectorio junto con mover el ts-loader debajo de los cargadores de estilo lo arregló para mí.
lux
4

Aviso si está utilizando la configuración de ruta tsconfig.json

Tenga en cuenta que si está utilizando estas soluciones junto con las rutas tsconfig para acortar su importación, necesitará una configuración adicional.

Si usa una ruta tsconfig como tal:

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

Entonces puedes hacer:

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

Luego, también debe agregar una configuración de resolución de módulos en su paquete web como tal:

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

Asegúrese de que la ruta esté configurada de acuerdo con su configuración.

Esto hace que webpack sepa dónde buscar, ya que cree que la nueva ruta de importación es en realidad un módulo, por lo que se establece de forma predeterminada en el directorio node_modules. Con esta configuración, sabe dónde buscar y la encuentra y la construcción funciona.

dbrody
fuente