Closed forest1102 closed 5 years ago
エラーの原因となっているos-locale
ですが、ちょっと事情があって普通にrequire
しているのではなく、require-uncachedモジュールを介してロードしています。
cheerio-httpcli/lib/client.jsから抜粋
// os-localeは最初に実行した段階で結果をcacheに持つので
// fetch()前に別のモジュールがos-localeを実行していた場合に
// オプション内容によっては予期しない結果が返ることがある
var requireUncached = require('require-uncached');
var osLocale = requireUncached('os-locale');
おそらくWebpack側ではrequire
は解決してもrequireUncached
の内部までは上手く解決できていないような気がします。(node_modules/os-locale/index.js
が存在するのにError: Cannot find module
になるのはよく分かりませんがWebpackの深層に潜るのはしんどそうだったので詳しくは追ってません)
で、解決方法なんですが、ちょっと強引ですがwebpack.config.js
を以下のようにします。
const path = require('path')
module.exports = {
mode: 'development',
entry: './src/index.ts',
target: 'node',
devtool: 'inline-source-map',
module: {
rules: [
// All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
{ test: /\.tsx?$/, loader: 'awesome-typescript-loader' },
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{ enforce: 'pre', test: /\.js$/, loader: 'source-map-loader' }
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
modules: ['node_modules'],
//不要 alias: {
//不要 'os-locale': './node_modules/os-locale'
//不要 }
},
//追加↓↓↓
externals: [{
'require-uncached': 'function(){}'
}],
//追加↑↑↑
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'lib'),
publicPath: './'
},
node: {
__dirname: false,
//不要 'os-locale': 'mock'
}
}
何をしているのかというと、require-uncached
を発見した時に読み込むモジュールを無理やり空関数を返すダミーモジュールに書き換えるように設定しています。
こうすると、bundle.js
内でrequire-uncached
は以下のようになります。
bundle.js
.
.
.
/***/ "querystring":
/*!******************************!*\
!*** external "querystring" ***!
\******************************/
/*! no static exports found */
/***/ (function(module, exports) {
module.exports = require("querystring"); // ←←← 普通はこうなる
/***/ }),
/***/ "require-uncached":
/*!*********************************!*\
!*** external "function () {}" ***!
\*********************************/
/*! no static exports found */
/***/ (function(module, exports) {
module.exports = function(){}; // ←←← 空関数に書き換わっている
/***/ }),
.
.
.
ただ、こうなるとrequire-uncached
が本来の役割を果たさなくなりますが、今回に限り問題はありません。
というのも、require-uncached
が空関数になった結果、
var requireUncached = require('require-uncached');
var osLocale = requireUncached('os-locale');
上記のrequireUncached
は空関数を返すダミーモジュールに、osLocale
は中身が空になります。
osLocale
変数は、その後、以下の箇所のみで使用されています。
// OSのロケールからAcceptLanguageを設定
if (! this.hasHeader('Accept-Language')) {
try {
// spawn: trueだとexecFileSyncを使うのでできればfalseにしたいが
// windowsだとロケールを取得できる確実な環境変数がなさそう
var locale = osLocale.sync({ spawn: (process.platform === 'win32') });
.
.
.
}
使用者側でAccept-Language
リクエストヘッダを指定していない場合に、osLocale
を使用してHTTPリクエスト時のAccept-Language
ヘッダの値を設定するようにしています。
したがって、index.ts
を以下のようにすればこのロジックを通らなくなり、問題は発生しません。
index.js
import * as client from 'cheerio-httpcli'
import { log } from 'util'
// ↓↓↓追加
client.set('headers', {
'Accept-Language': 'ja,en-US'
})
// ↑↑↑追加
client
.fetch('https://hogehoge.com')
.then(({ $ }) => console.log($('body').text()))
.catch(err => console.log(err)
というように、今回のケースのみに使える強引な対処方法ですが、他に解決方法は浮かばなかったのでこちらの方法を提案しましたがいかがでしょうか。
今回提案した強引なexternals
の指定方法ですが、Webpackのexternals
関連のソースを確認した所、一応想定内の指定として処理されているような感じだったので大丈夫だとは思いますが、もしかしたら将来的にこの方法が通用しなくなる可能性はあります。
回答いただきありがとうございます。 成功はしたのですが、index.tsをエクスポートして他のファイルでインポートしようとwebpack.config.jsを変更すると以下のようにエラーが出るので、申し訳ないですが見ていただけますか?
Error: Cannot find module 'function(){}'
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:581:15)
at Function.Module._load (internal/modules/cjs/loader.js:507:25)
at Module.require (internal/modules/cjs/loader.js:637:17)
at require (internal/modules/cjs/helpers.js:22:18)
at Object.require-uncached (**/sidework/robots/lib/bundle.js:161152:18)
at __webpack_require__ (**/sidework/robots/lib/bundle.js:21:30)
at Object../node_modules/cheerio-httpcli/lib/client.js (**/sidework/robots/lib/bundle.js:48684:23)
at __webpack_require__ (**/sidework/robots/lib/bundle.js:21:30)
at Object../node_modules/cheerio-httpcli/lib/core.js (**/sidework/robots/lib/bundle.js:49295:21)
at __webpack_require__ (**/sidework/robots/lib/bundle.js:21:30)
const path = require('path')
module.exports = {
mode: 'development',
entry: './index.js',
target: 'node',
devtool: 'inline-source-map',
module: {
rules: [
// All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
{ test: /\.tsx?$/, loader: 'awesome-typescript-loader' },
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{ enforce: 'pre', test: /\.js$/, loader: 'source-map-loader' }
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
modules: ['node_modules']
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'lib'),
publicPath: './',
// ↓追加
library: 'default',
libraryTarget: 'commonjs'
// ↑追加
},
node: {
__dirname: false
},
externals: [
{
'require-uncached': 'function(){}'
}
]
}
library関連の指定を入れるとWebpackの挙動が変わるっぽいです。
以下のようにすればいけそうです。
externals: [
{
'require-uncached': 'var function(){}'
}
]
いずれにしても対応としては何か胡散臭いので、近々cheerio-httpcli側に修正を入れてWebpack環境下でもある程度動くように調整はしようと思います。
あと、今回のWebpack側の設定で何とかする対応だと、cheerio-httpcliの自動エンコーディング判定が上手く動かないようで、UTF-8以外のページを見に行くとエラーになります。
それに関しては現段階ではどうにもならないので、cheerio-httpcliの修正をお待ちください(今週中になんとか)。
Webpackに特殊な設定をしなくてもある程度動作するように修正したバージョンをnpmにアップしました(0.7.4)。
Webpackの仕様上、どうにもならない制限は以下にまとめてあります。
出来る限りの対応が完了したのでこちらは一旦closeします。 また何か不具合など発生したらお知らせいただければと思います。
ありがとうございます。 今回は親切に教えてくださりありがとうございました。 これからも色々な場面で使っていく予定ですのでまたお世話になるかもしれません。
問題
cheerio-httpcliをインポートしたファイルをWebpackでバンドルしたものを実行しようとすると以下のようにエラーが出てしまいます。 何か解決法はありますか?
webpack.config.js
./src/index.ts