[{開発日記}]19歳エンジニア、ReactでUIコンポーネントライブラリをつくる
つくってるライブラリ
5月31日
動機はなんだったかな。
高校時代は個人開発ばかりやっていたから、バックエンドとフロントエンドのどっちの道に進みたいかなんか決まっていなくてね…
圧倒的にバックエンドの方が得意な気はしていたけれど、どっちもそこそこ好きだったし、フルスタックエンジニアになりたいととりあえず言っておいていた時期がある。
ただ、いろいろあってフロントエンドのプロになりたいと強く思うようになった。
まあきっかけとしては、前職でHTML/CSSの社内勉強会講師を担当したことだろうな。
なんというか、人間に寄り添う仕事がしたいのだ。ハードウェアの都合よりも、人間の都合をひたすら考えたい。適当に済ませたらすぐにユーザにバレて不満に直結する緊張感が追求欲を掻き立てる。ここに情熱を置きたいと思った。
2022年6月13日〜26日
諸事情あってPCが使えなかったこの期間は、ひたすら本を読み漁った。
・HTML仕様書
・インクルーシブデザイン
・Atomic Design
・TypeScript
・CSS設計
・OOUIデザイン
・アクセシビリティ(WAI-ARIA)
・UI/UX
あたりを勉強。
2022年6月27日
ようやくPC解禁。
リポジトリを作って、開発環境(というかnpmパッケージの雛形?)をセットアップ。
2022年6月28日〜29日
SimpleMindでコンセプトマップを作った。
このライブラリのコンセプト(実装観点)や網羅したいカテゴリを書き出して、それぞれに属するコンポーネントを挙げていって、propsで実現可能にしたいバリエーションを整理。
かなり膨大…
2022年6月29日〜30日
SImpleMindでAtomic Design風コンポーネント実装パターンのチートシートを作成。
これとTSの本を見ながらならかなりサクサクいい感じのコンポーネントを量産できるだろう。
せっかくなので公開したいけど、形式がねえ…
2022年6月30日
引き続きSimpleMindで、UIコンポーネントのテスト手法を整理。
フロントエンドのテストって大変なのね…
これもそのうち公開したい。
ようやく具体的な実装を考えていく。
パッケージアーキテクチャ
- yarn1のワークスペースでmonorepo構成とする
- ディレクトリ構成はお作法がわからないのでmuiを参考にする
props
- chakra uiに似たシンタックスでスタイルを制御できるようにする(csstypesで型づけ)
スタイリング
- styled-component
コンポーネントリスト
- storybook
ライブラリのロゴとサイト(今はまだほぼ私の履歴書…)をつくった。
monorepoのためにlernaを導入。
npx nx graph
でpackage構造がグラフで見られるらしい。早く使ってみたい。
そういえば、storybookってどのディレクトリに入れるべきなのか…
リポジトリ名変えた。
npx lerna bootstrap
ってpackage増えるたびにやるのかしら。
2022年7月1日
ついにコンポーネント実装に入る。
ディレクトリ構成の時点でひたすら悩む…どういうパッケージ分割にすれば使いやすいのか…「あれはどこに入っているんだっけ?」と迷いを持たせたら負けかな。
とりあえず慣れよう。
共通で使うコンポーネントはcoreパッケージにまとめることにする。
Lerna覚書
sampleパッケージを内部に作成
npx lerna create sample
特定のパッケージにインストール
cd パッケージ
yarn add -D @types/react-dom
共通パッケージをインストール
yarn add -W --dev @types/react-dom
参照できるように?
npx lerna bootstrap
特定のパッケージのみ実行
lerna run --scope @react-polyhexui/styling-patch build
monorepoについて、いろいろと勘違いをしていた。
polyhexUIパッケージ(ルート)を公開するのではなく、その中に作ったパッケージの一つをメインパッケージとして公開するのね…
private: true
を書き忘れていたせいでlernaもyarnも使えない状況に陥っていた汗
そしてcsstypeをモジュール型拡張して独自のcss.d.tsを作りたくて躍起になっている。
やりたいこととしてはこれ。キャメルケースprops指定でスタイリングができるようにしたいのだ。
全CSSプロパティに対応した上で、いくつかの短縮記法も定義したい。
ちなみにTypeScriptを書いた経験は一切ない。
写経すらせずにいきなりライブラリを書き始める怠け者である。というか、目標がある程度高くないと勉強のモチベーションが上がらないタチなのだ。
型拡張のお勉強。
tsconfig.jsonの謎のwarningを消したい。
これ便利…
これで解決した。tsファイルがあるディレクトリに移しただけ。
at-rule propsの命名どうしよう…と思って今更だけどちょっと確認。
やっと見つけた。
これを見つつミックスインを作るのだ。
インターフェースの結合と継承って何が違うんだ…?
- 結合は同名同士
- 継承は拡張版に別名をつける
…みたいなイメージでいいのかなあ…
lerna、やっぱりルートに移動してから全体にパッケージをインストールしなければimportできないっぽい。
そしてimport問題はtsconfigの設定でかなり揉めた…
2022年7月2日
Chakra UI風スタイリング用Propsの定義にあたって、いくつかの問題点が浮上している。
- 全CSSプロパティに対応したい
- が、csstypeはテキスト用プロパティなどの分類まではしてくれない
- CSS modulesというMDNの規格に沿って自動で分類したい
- ということで、mdn-dataに含まれるJSONデータをgroupsの値ごとに分類しよう
この実現のために躍起になっていた昨日。
関数型プログラミングの本を読み漁ったり、TypeScript PlayGroundで型パズルを試したりと、スマートな記法を模索していたが、結局疲れ果てていつの間にか寝ていた。
朝起きたら頭痛が酷かったので、もうJSON解析ライブラリを探す方向に切り替える。
そうしたら見つけた…JSONをSQL likeに解析するライブラリを!!!!!
ちなみに途中までいじったTypeScript PlayGroundを一応Save目的で貼っておく。
テーブルに値固定のカラムを作りたくていろいろ揉めた…
結局定数SELECTをFROM句のサブクエリとして埋め込むことで解決。
ひと段落。
コマンドパレットからTSサーバー再起動でやっと治ったにゃ。素晴らしい
欲しかったデータセットがついに手に入りました。
これからpropsとして採用するものを選定して型を定義していくにゃ
2022年7月3日
CSSsyntaxの分類も終わり、いよいよpropsを作成してcomponentから呼び出せるようにするフェーズへ突入。
その辺りはコンポーネントを実装しながら作っていった方が良さげ。とりあえずStorybookとReactの環境を作らないとね。
StyletronのこのAPIを使ってどうにかカスタムPropsのスタイルを定義できないかな。と画策中。
instance.renderStyle({
color: "red",
fontSize: "12px",
});
// → "css-abc"
セレクタの変換にはこのパッケージを使おうと思ってる。
本当にPropsでのスタイリングでいいのか?と考えてみたり。
どんどんタグが汚れていくのはinline styleやutility firstと変わらない。
かといってstyled-componentも対応関係を追うのが面倒くさい。
CSS modulesはなんか怖い。(非推奨予告も出てるし)
JSXに投入するの(JSS?)もなんか嫌だ…
っていうか、私はそもそもにしてVueよりReact派ではあるが、スタイリングに関しては圧倒的にVueに魅入っている。
JSとHTMLを融合させるメリットは大いに感じるし、テンプレートエンジンなんかよりもJSXの方が圧倒的に良きソリューションだとは思うのだが、そこにCSSまでぶちこもうとするのは個人的に地雷(受け付けない代物)なんだ。
せめてCSSは分けたい。でもどうにかタグ構造との対応をわかりやすくしたい。
そうやって云々と考えていたら、降りてきた。
こんな感じでReactのスタイリングができたらなあ〜という新記法を思いついた。
右がJSX、左がXML拡張の独自記法
これでJS+HTML(JSX)とCSSの分離が実現される & デザイン的にも対応がわかりやすい
( ※JSで変化させるCSSはこれまで通りJSXに書くことになるが…)
技術的に実現できないかちょっと真剣に考えている
最低限欲しい要件としては
- 右のHTMLコードから左のコードの雛形をコマンドで作成できるようにする
- watchモードのCLIツールを作成して、右のHTML構造を変更したら左にも反映されるようにする
- StylePatchタグ内全体にハッシュ値のクラス名を付与して、タグ内に定義したCSSがタグ外の要素に影響しないようにする(この辺りはStyletronとかの力も借りつつ。)
- StylePatchタグ内ではカスケードされる(1つのpにmarginを設定したらすべてのpにmarginが継承される、といった具合。ただしclass付の場合はその要素のみ、明示的にその要素のみに適用させたい場合は!localをつける)
もちろんParserとCLIツールは自分で書かないといかん。
この案の実現に向けてちょっと全力投球してみたい。(どんどん薄くなるUIコンポーネントの影…)
カリー化やクロージャは感覚でなんとなく使っているけど、これから重い処理を書くわけだしもっと綺麗なコードが書けるようになりたいにゃ
というわけで一旦開発は中断して、関数型プログラミングの基礎を読むことにするにゃ
構文解析木やドロドロ正規表現解析の綺麗な書き方についてもしっかり勉強するにゃ…
関数型プログラミング学習用のリポジトリつくった。
今日はもう寝る。
2022年7月4日
TypeScript移植をするには知識が足りなさすぎる…
本当につくづく思う、TypeScriptの世界ではまだまだスライムしか倒せないひよっこなのだ。
というわけでちゃんと勉強しよう。
プログラミングTypeScriptを読むにゃ。
はい作成。
写経を楽しめるのは(プログラマ歴的に)若いうちの特権だよなあ。
出てくる出てくる。うわその記法使うんか…とか。
おかげで改造なり移植なり工夫を凝らさないとろくに楽しめなくなってしまった。
初級者卒業の一つの基準は、自分の頭で考えるようになることかもね。
大人にならないと批判心は生まれないのと同じ。
というか、新しい言語やパラダイムに触れる時も、カタログ感覚でチラ見しつつ何か作ればいいじゃんと思ってしまう。
どうせ経験済みのツールとの差分を捉えるだけだから、入門書なんて退屈で冗長で読んでられないのよね。
もうその域まで来たんだな。そろそろ中級者を名乗ってもいいだろうか。
てなわけで独自開発に戻るにゃ
って言ってもまだまだ下調べも必要だけどね
doc - Markdownでコンポーネントの使用法とプロパティを文書化する
links - ストーリーをリンクして、UIコンポーネントを使用してデモとプロトタイプを作成
actions - インタラクティブ要素でアクションが実行されたときにUIフィードバックを取得
viewport - ビューポートのサイズと向きを調整して、レスポンシブコンポーネントを構築
controls - UIでコンポーネント入力を動的に操作する
backgrounds - 背景を切り替えて、さまざまな設定でコンポーネントを表示
toolbars - レンダリングを制御する独自のツールバーアイテムを作成
storysourse - storrybookの画面からstoryを確認
storybook-addon-html - コンポーネントのHTMLタグを確認
StoryShots - Jestを使用して、すべてのストーリーのコードスナップショットを自動的に作成
Accessibility - コンポーネントがWebアクセシビリティ標準に準拠しているかどうかをテスト
Console - ログ、エラー、警告などのコンソール出力を表示
rollupの沼にハマっているなう。
戦いが終わった。
結局ビルド構成はrollup + esbuild-pluginに落ち着きました。
2022年7月5日
昨日はstorybookの導入とビルド構成だけで終わった…
いよいよコンポーネントを作りつつ、独自のスタイリングシステムを実装していく。
独自Providerで駆動するシステムということもあり、いろいろとアプリでの挙動が気になるので、create-react-appによる実地検証環境も構築した。
2022年7月6日
昨日はなんか自動化スクリプトを書いて終わったw
そろそろプログラミング再開してから一週間が経つかな?
一週間の疲労と不摂生が祟って、18時には眠っちゃいました。
そして今日は手始めにmerge自動化スクリプトを書いた…んだけど、これVScode拡張入れればGUI操作でストレスなくできたかも…?
まあいいや、私はとりあえずzxを使いたかったのだ笑
関数型もいいけど、大規模になるとやっぱりオブジェクト指向で書きたくなるよな
構成はOOP、局所的なデザインは関数型…くらいがちょうどいいかも
2022年7月7日
独自スタイリング記法をCSSに変換するプリプロセッサを書いているが、ネスト対応がうまくいかないの図
再帰でネスト対応するよりも最初からネスト部分を分離して別途処理した方がいい気が
デカいプログラムを書く時は各モジュールの枠組みをオブジェクト指向で書きたくなるが、その内部の各メソッドを洗練させるには関数型プログラミングが不可欠…という役割分担を意識すればだいぶ綺麗なコードが書けるんじゃないかという独自理論
とりあえずいい加減に関数型(LodashとRamda)を勉強しつつUtilityツールを作っては実装イメージを固めていきたい。
2022年7月8日
関数型がわからん…血肉になるまでが大変だ
プログラミング言語の中のHaskell、物理学でいう量子力学的なポジションなのでは(難易度的に)
2022年7月9日
ようやく使えるようになってきたparsar-ts…!!
意外と簡単にパーサが書けて驚いている…
2022年7月11日
更新が滞っているのには事情がある。
ちょっと疲れてしまったのだ。私は関数型の沼に引き摺り込まれている。
まあいいや。冷静になって描き直そうと思うから、落書きの痕跡はここに貼っておくよ。
import * as S from 'parser-ts/string'
import * as P from 'parser-ts/Parser'
import * as C from 'parser-ts/char'
import { run } from 'parser-ts/code-frame'
import { apply, pipe } from 'fp-ts/function'
import { map, foldMap } from 'fp-ts/Array'
import { match } from 'ts-pattern'
import _ from 'lodash'
import shell from 'shelljs'
const { ShellString } = shell
import jsonFormat from 'json-format'
const config_jsonFormat = {
type: 'space',
size: 2,
}
const styp = `<StylePatch>
<span>
{{
backgroundColor: '#1a1a1a',
borderRadius: '2px',
color: 'white',
display: 'inline-block',
fontSize: '0.8rem',
padding: '0.4rem 0.5rem',
position: 'relative',
__after: {
borderColor: '#1a1a1a transparent transparent transparent',
borderStyle: 'solid',
borderWidth: '3px 3px 0 3px',
bottom: '0',
content: '',
display: 'block',
height: '0',
left: '50%',
position: 'absolute',
transform: 'translate(-50%, 100%)',
width: '0',
},
}}
</span>
</StylePatch>
`.replaceAll(/[\r\n\t]/g, '')
interface AstNode extends Record<string, string> {
readonly kind:
| 'OPEN_componentTag'
| 'OPEN_htmlTag'
| 'CLOSE_tag'
| 'CSS_property'
| 'CSS_value'
| 'START_css'
| 'END_css'
| 'BEGIN_nesting'
| 'END_nesting'
| 'BEGIN_stypFile'
| 'END_stypFile'
| 'empty'
readonly value: string
}
type Ast = Array<AstNode>
const spaceTrim = P.surroundedBy(S.spaces)
const startFile: P.Parser<string, AstNode> = pipe(
S.string('<StylePatch>'),
P.map(value => ({
kind: 'BEGIN_stypFile',
value: value.slice(1, -1),
}))
)
const endFile: P.Parser<string, AstNode> = pipe(
S.string('</StylePatch>'),
P.map(value => ({
kind: 'END_stypFile',
value: value.slice(2, -1),
}))
)
const openingComponentTag: P.Parser<string, AstNode> = pipe(
P.manyTill(
pipe(
C.char('<'),
P.alt(() => C.letter)
),
C.char('>')
),
P.map(value => {
return {
kind: 'OPEN_componentTag',
value: value.slice(1).join(''),
}
})
)
const openingHtmlTag: P.Parser<string, AstNode> = pipe(
P.manyTill(
pipe(
C.char('<'),
P.alt(() => C.lower)
),
C.char('>')
),
P.map(value => {
return {
kind: 'OPEN_htmlTag',
value: value.slice(1).join(''),
}
})
)
const closingTag: P.Parser<string, AstNode> = pipe(
P.manyTill(
pipe(
S.string('</'),
P.alt(() => C.letter)
),
C.char('>')
),
P.map(value => ({
kind: 'CLOSE_tag',
value: value.slice(1).join(''),
}))
)
const cssProperty: P.Parser<string, AstNode> = pipe(
P.manyTill(
pipe(
C.letter,
P.alt(() => C.oneOf(["'", '_'].join('')))
),
C.char(':')
),
P.map(value => ({
kind: 'CSS_property',
value: value.join(''),
}))
)
const cssPropertyValue: P.Parser<string, AstNode> = pipe(
P.manyTill(
pipe(
C.alphanum,
P.alt(() =>
C.oneOf(
["'", '"', ':', '#', '.', '-', '%', ' ', '(', ')', ','].join('')
)
)
),
S.string("',")
),
P.map(value => ({
kind: 'CSS_value',
value: value.join('').replaceAll(/\'/g, ''),
}))
)
const startStylePatch: P.Parser<string, AstNode> = pipe(
S.string('{{'),
P.map(value => ({
kind: 'START_css',
value: value,
}))
)
const endStylePatch: P.Parser<string, AstNode> = pipe(
S.string('}}'),
P.map(value => ({
kind: 'END_css',
value: value,
}))
)
const startNestCss: P.Parser<string, AstNode> = pipe(
S.string('{'),
P.map(value => ({
kind: 'BEGIN_nesting',
value: value,
}))
)
const endNestCss: P.Parser<string, AstNode> = pipe(
S.string('},'),
P.alt(() => C.char('}')),
P.map(value => ({
kind: 'END_nesting',
value: value.replaceAll(',', ''),
}))
)
const parser: P.Parser<string, AstNode> = pipe(
startFile,
P.alt(() => spaceTrim(endFile)),
P.alt(() => spaceTrim(openingHtmlTag)),
P.alt(() => spaceTrim(openingComponentTag)),
P.alt(() => spaceTrim(closingTag)),
P.alt(() => spaceTrim(cssProperty)),
P.alt(() => spaceTrim(cssPropertyValue)),
P.alt(() => spaceTrim(startStylePatch)),
P.alt(() => spaceTrim(endStylePatch)),
P.alt(() => spaceTrim(startNestCss)),
P.alt(() => spaceTrim(endNestCss)),
P.map(value => {
return value
})
)
const result = run(P.many(parser), styp)
const ast = result._tag === 'Right' ? result.right : []
import * as Ar from 'fp-ts/Array'
import { last } from 'fp-ts/Semigroup'
import { Foldable, zip } from 'fp-ts/Array'
import { identity } from 'fp-ts/function'
import { fromFoldableMap } from 'fp-ts/Record'
import * as Rec from 'fp-ts/Record'
import { Magma } from 'fp-ts/Magma'
const m2: Magma<string> = { concat: (x: string) => x }
type AstNodeAddID = {
id: string
forest?: Array<TREE.Tree<unknown>>
} & AstNode
const syntaxRecords: Array<Record<string, string>> = pipe(
ast,
Ar.mapWithIndex((i: number, record: Record<string, string>) =>
Rec.union(m2)(record)({ id: `${i}` })
)
)
const parseAreaSyntaxRecord = pipe(
syntaxRecords,
Ar.dropRight(1),
Ar.dropLeft(1)
) as Array<AstNodeAddID>
import * as TREE from 'fp-ts/Tree'
import * as ARRAY from 'fp-ts/Array'
import * as E from 'fp-ts/Either'
import * as STATE from 'fp-ts/State'
const zipObject =
<K extends string, A>(keys: Array<K>) =>
(values: Array<A>): Record<K, A> =>
fromFoldableMap(last<A>(), Foldable)(zip(keys, values), identity)
type BuildStatus = E.Either<
TREE.Forest<AstNodeAddID> | never,
TREE.Forest<AstNodeAddID> | never
>
type AstTree = STATE.State<
BuildStatus,
TREE.Tree<{ readonly value: AstNodeAddID } | AstNodeAddID>
>
const castTree = pipe(
parseAreaSyntaxRecord,
ARRAY.map(r => zipObject(['value', 'forest'])([r, []]))
) as TREE.Forest<AstNodeAddID>
const isolateTarget = (records: TREE.Forest<AstNodeAddID>) => {
const [[{ value }], forest] = pipe(records, ARRAY.splitAt(1))
const test = TREE.fold(
(root: AstNodeAddID, forest: TREE.Forest<AstNodeAddID>) =>
TREE.make(root, forest)
)
const res = test({
value: value,
forest: forest,
})
return res
}
import * as NUMBER from 'fp-ts/number'
const matchAnyOne = (s: string) => (ary: Array<string>) => {
const matchCount = pipe(
ary,
ARRAY.filter((item: string) => s === item),
ARRAY.size
)
return !NUMBER.Eq.equals(matchCount, 0)
}
const nestStartKeywords = ['OPEN_htmlTag', 'START_css', 'BEGIN_nesting']
// prettier-ignore
const matchController =
(forest: TREE.Forest<AstNodeAddID>): AstTree => {
const { kind, id } = forest[0].value
return pipe(
kind,
E.fromPredicate(
kind => matchAnyOne(kind)(nestStartKeywords),
() => {}
),
E.match(
() => ((_s: BuildStatus) => [TREE.bindTo('value')(forest[0]), E.left(forest.slice(1))]),
() => {
const nextTarget = pipe(
forest,
isolateTarget,
)
//console.log(currTree)
return (_s: BuildStatus) => [TREE.bindTo('value')(nextTarget), E.right(nextTarget.forest)]
}
)
)
}
const astBuildController = (state: AstTree): AstTree => {
const [currTree, status] = state(E.right(castTree))
return pipe(
status,
E.match(
() => state,
prevForest => {
return pipe(prevForest, matchController, astBuildController)
}
)
)
}
//const [t1, t2] = STATE.traverseArray(matchController)([E.right(castTree)])
const [output, finalStatus] = astBuildController(() => [
castTree[0],
E.right(castTree.slice(1)),
])(E.right(castTree))
const test = parseAreaSyntaxRecord
console.log(test)
//
console.log(jsonFormat(test, config_jsonFormat))
new ShellString(jsonFormat(test, config_jsonFormat)).to('tmp/buildinfTree.json')
// prettier-ignore
const css = pipe(
ast as Ast,
map(({ kind, value }: AstNode) => {
return match(kind)
.with('BEGIN_stypFile', () => '')
.with('END_stypFile', () => '')
.with('OPEN_componentTag', () => value + '{')
.with('OPEN_htmlTag', () => value + '{')
.with('CLOSE_tag', () => '}')
.with('CSS_property', () => value + ':')
.with('CSS_value', () => `"${value}"` + ';')
.with('START_css', () => '')
.with('END_css', () => '')
.with('BEGIN_nesting', () => value)
.with('END_nesting', () => value)
.exhaustive
}
),
foldMap({ concat: (a: string, b: string) => a + b, empty: '' })((s: string) => s)
)
const normalizedCss = css
.replaceAll('_at_', '@')
.replaceAll('__', '&::')
.replaceAll('_', '&:')
new ShellString(normalizedCss).to('tmp/pseudoCSS.scss')
2022年7月12日
一旦不慣れな関数型Syntaxを封印して本気出したら、独自記法からの変換処理(パーサーとレキサー)も4時間で書き終えちゃった件。
オリジナルのReactスタイリングシステム、残りの工程はCLIコマンド作成だ。
- .prettierignoreとかを作成して環境整備するコマンド
styp setup
- jsxからスタイリングファイルの雛形を生成するコマンド
styp init
- スタイリングファイルに書いたスタイルをJSXに反映させるコマンド
styp patch
- jsxの変更をスタイリングファイルと同期させるコマンド
styp watch --from=jsx
- stypファイルの変更をjsxファイルに反映させるコマンド
styp watch --from=styp
その前にちょっとリファクタしてマシにするかー。モジュールとして分離もしないとだし
モックアップを描くのって大事だ。
これからはとりあえず実現させてからコードのデザインをどうにかするスタンスでいこう。
最初から綺麗なコードを描けたらいいんだけど、残念なことにどうにも私はまだその域には達していないらしい。
2022年7月14日
不要になったごみにゃ
const parse =
(stopKeyword: string) =>
(buffer: Buffer): ParseResult => {
let prevChar: Array<C.Char> = Array(stopKeyword.length)
return pipe(
buffer,
STREAM.stream,
P.takeUntil((c: C.Char) => {
prevChar.push(c)
//console.log(prevChar)
return prevChar.join('') === stopKeyword
}),
EITHER.match(
() => ({
rest: buffer,
found: '',
}),
obj => {
const [stop, ...rest] = ARRAY.dropLeft(obj.next.cursor)(buffer)
const findString = obj.value.join('') + stop
return {
rest: ARRAY.dropLeftWhile((c: C.Char) => c === ' ')(rest),
found: findString,
}
}
)
)
}
const finder = {
beginFile: parse('<StylePatch>'),
endFile: parse('</StylePatch>'),
tagStart: parse('<'),
tagEnd: parse('>'),
beginCss: parse('{'),
endCss: parse('}'),
endProperty: parse(':'),
endValue: parse(','),
}
let tokensArray: Array<string> = []
const nextParse = (rest: Buffer) => {
return (
match(_.first(rest))
.with('<', () => parse('>')(rest))
//.with('{', () => parse('{')(rest))
//.with("'", () => finder.endValue(rest))
.otherwise(() => parse('}}')(rest))
)
}
const saveContinued = (buffer: Buffer): Buffer => {
const { rest, found } = nextParse(buffer)
//console.log(_.first(rest), found)
tokensArray.push(found)
return _.isEmpty(rest) ? [] : saveContinued(rest)
}
pipe([...rawStyp], finder.beginFile, obj => obj.rest, saveContinued)
console.log(tokensArray, '\n')
2022年7月15日
import * as STORE from 'fp-ts/Store'
import * as STATE from 'fp-ts/State'
import * as TRACED from 'fp-ts/Traced'
interface Token {
type: string
value: string
}
interface Parser {
// 現在の位置のトークンを取得
readonly peek: (pos: number) => Token
// 現在どこまで読んだか
readonly pos: number
}
const tokenSequenceParser: Parser = {
peek: (pos: number) => tokenSequence[pos],
pos: 0,
}
const ast = []
const astNodeBuilder = {
tag: () => {
/**
map: <A, B>(f: (a: A) => B) => <E>(fa: State<E, A>) => State<E, B>
*/
const pushTokenStack = (tokens: string[]) =>
tokens.push(tokenSequenceParser.peek(tokenSequenceParser.pos).value)
const judgeFinish = (isFinished: boolean) => {
if (isFinished) {
return [pushTokenStack, isFinished]
} else {
return [pushTokenStack, isFinished]
}
}
const stack = STATE.map(pushTokenStack)
console.log(stack)
},
}
const pushTokenStack = (tokens: string[]) => {
tokens.push(tokenSequenceParser.peek(tokenSequenceParser.pos).value)
return tokens
}
const judgeFinish = (isFinished: boolean) => {
if (!isFinished) {
STORE.seeks((pos: number) => pos + 1)(tokenSequenceParser)
STATE.map(pushTokenStack)(STATE.get())
console.log(STATE.map(pushTokenStack)(STATE.get())([]))
}
return [[], isFinished] as [string[], boolean]
}
const stack = STATE.map(pushTokenStack)
console.log(stack(judgeFinish)(false))
const tokenMatcher = (token: Token, _cursor: number) => {
return match(token).with({ value: '<', type: 'JSXPunctuator' }, () =>
astNodeBuilder.tag()
)
}
const res = tokenSequence.map((token, _cursor) => tokenMatcher(token, _cursor))
//console.log(res)