オプショナルチェイニングはどうでも良いが、エラーがエグくて判明まで時間がかかったのでメモしとく
ことの発端
こんなコードを書いてみて、Storybookで確認しようとしたら、エラーになる。
<template>
<canvas ref="canvas"></canvas>
</template>
<script lang="ts">
import { defineComponent, ref, watch } from "@vue/composition-api";
interface Props {}
export default defineComponent<Props>({
name: "WebGL",
props: {},
setup: (props, ctx) => {
const canvas = ref<HTMLCanvasElement>();
watch(canvas, () => {
const gl = canvas.value?.getContext("webgl");
if (gl) {
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
}
});
return { canvas };
}
});
</script>
ERROR in ./src/components/_sandbox/WebGL.vue?vue&type=script&lang=ts& (./node_modules/vue-docgen-loader/lib??ref--12!./node_modules/ts-loader??ref--4-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/_sandbox/WebGL.vue?vue&type=script&lang=ts&) 8:36
Module parse failed: Unexpected token (8:36)
File was processed with these loaders:
* ./node_modules/vue-docgen-loader/lib/index.js
* ./node_modules/vue-docgen-loader/lib/index.js
* ./node_modules/ts-loader/index.js
* ./node_modules/vue-loader/lib/index.js
You may need an additional loader to handle the result of these loaders.
| const canvas = ref();
| watch(canvas, () => {
> const gl = canvas.value?.getContext("webgl");
| if (gl) {
| gl.clearColor(0.0, 0.0, 0.0, 1.0);
@ ./src/components/_sandbox/WebGL.vue?vue&type=script&lang=ts& 1:0-236 1:252-255 1:257-490 1:257-490
@ ./src/components/_sandbox/WebGL.vue
@ ./src/components/_sandbox/WebGL.stories.ts
@ ./src sync ^\.(?:(?:^|\/|(?:(?:(?!(?:^|\/)\.).)*?)\/)(?!\.)(?=.)[^/]*?\.stories\.(js|jsx|ts|tsx))$
@ ./.storybook/generated-stories-entry.js
@ multi ./node_modules/@storybook/core/dist/server/common/polyfills.js ./node_modules/@storybook/core/dist/server/preview/globals.js ./.storybook/storybook-init-framework-entry.js ./node_modules/@storybook/addon-docs/dist/frameworks/common/config.js-generated-other-entry.js ./node_modules/@storybook/addon-docs/dist/frameworks/vue/config.js-generated-other-entry.js ./node_modules/@storybook/addon-links/dist/preset/addDecorator.js-generated-other-entry.js ./node_modules/@storybook/addon-actions/dist/preset/addDecorator.js-generated-other-entry.js ./node_modules/@storybook/addon-actions/dist/preset/addArgs.js-generated-other-entry.js ./node_modules/@storybook/addon-backgrounds/dist/preset/addDecorator.js-generated-other-entry.js ./node_modules/@storybook/addon-backgrounds/dist/preset/addParameter.js-generated-other-entry.js ./node_modules/@storybook/addon-knobs/dist/preset/addDecorator.js-generated-other-entry.js ./.storybook/preview.js-generated-config-entry.js ./.storybook/generated-stories-entry.js (webpack)-hot-middleware/client.js?reload=true&quiet=false&noInfo=undefined
ここで、 Optional Chaining が問題だと気付くまで小一時間。
前にも同じ事があったような。。。
Optional Chaining って今どんな状況?
ちなみに、plugin-proposal-optional-chaining
で簡単に解決することは気付いていたが、もしかるすと、自分が時代遅れな事をやろうとしているんじゃないかと、もうちょい別の方法を模索する。。。
TypeScript の Optional Chaining
そもそも TypeScript 3.7 で導入された機能ってことは知っているんだけど...
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html
ふむふむ、package.jsonのTypeScriptは3.9.3だから良いとして。。。
ECMAScript の Optional Chaining
あ〜、そもそも ECMAScript で採用ってされてるんだっけ? って事で調べてみると。。。
お〜 ECMAScript 2020 で採用って記事が出てるやん!
で、早速、ECMAに行ってみて。。。 「ECMA-262 11th Edition / June 2020」 を見てみる。
https://www.ecma-international.org/wp-content/uploads/ECMA-262.pdf
12.3.9 で定義されてますね。ここでは、「Optional Chaining」じゃなくて「Optional Chains」って名称になったんか。
Mozilla の Optional Chaining
さて、次は、Mozillaでサポート状況を調べてみますか。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Optional_chaining
サポート状況は分かったが、仕様の項目に「オプショナルチェイニング" 演算子の提案」ってなんだ?? って、ことで見てみる。
https://tc39.es/proposal-optional-chaining/#sec-scope
あれれ?? まだ提案段階なの??
う〜ん、Mozillaの更新が遅れているのかな?(汗)
Babel の Optional Chaining
あ、最後に Babel 確認しとかないと。。。
https://babeljs.io/docs/en/
ES2015以降であれば「Babel has support for the latest version of JavaScript through syntax transformers.」って、何これ? 魔法なのか?? 今度調べておかないと。。。(疲)
まぁ、いい。。。大丈夫そうだ。。。 って、今回の件はコレが臭いのだけど、大丈夫なのか??
結果どうする?
Optional Chaining のサポート状況云々より、そもそも babel が ES2015 で生きている以上plugin-proposal-optional-chaining
を追加するしか無いと判断したので、本家を調べる。
https://storybook.js.org/docs/react/configure/babel
なるほど。。。
で、現在の設定を調べてみたいので、.storybook/main.js
でこんなことをしてみる。
module.exports = {
babel: async (options) => {
console.log(JSON.stringify(options));
return options;
}
};
そしたら何と! 既に plugin-proposal-optional-chaining
が設定されていると。。。
ということは。。。
どういうことだ?
あれ? babel-loader 動いてない??
んじゃ〜って事で、こうしてみる。
module.exports = {
webpackFinal: async (config) => {
config.module.rules.push({
test: /\.(tsx|ts)$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader"
},
{
loader: "ts-loader",
options: {
transpileOnly: true
}
}
]
});
}
ようやく動いた。。。 orz
いや、違う。。。
何だか、納得できない。。。
ts-loader 動いてるんだから、babel云々じゃなくね?
んじゃ、tsconfig.json
だわ。
って、ことで、.storybook/main.js
の変更は破棄して、tsconfig.json
を覗いてみる。
あ。。。 targetが esnext やんけ。。。
ちゅうことで、target を ES5 に変更して無事解決。
つまり。。。
tsconfig.json
の target
を "ES5"
にする。
ふぅ。。。
う〜ん
実はまだ解せないので、もうちょい調べる。
そもそも、誰がエラーを出していたのか、もう一度振り返ること。