Open8

Snowpack v3を試す

ピン留めされたアイテム
uuyu-guuyu-g

Snowpack v3の試行錯誤ログ

uuyu-guuyu-g

Javascript APIを試す

dev serverを起動する

CLIではなくjsからsnowpackのdev serverを起動する。

server.js
const { startServer, loadConfiguration } = require('snowpack');

async function main() {
  const config = await loadConfiguration({}, './snowpack.config.js');
  startServer({ config, lockfile: null });
}

main();
$ node server.js

startServer({ config, lockfile: null })は、2021/01/23現在ドキュメントでは config だけ設定すれば良いように書かれているが、loackfile も設定が必要。nullで良いので将来省略可能になりそうな気はする。

uuyu-guuyu-g

SSR

https://www.snowpack.dev/guides/server-side-render#option-3%3A-server-side-rendering-(ssr)

公式サイトのサンプルを動く形にしてみる。

1. create-snowpack-appでReactのプロジェクトを作成

# typescriptじゃなくてもうごくはず
$ npx create-snowpack-app react-snowpack-ts --template @snowpack/app-template-react-typescript

2. buildする

$ npm build

3. server.jsを作成

ほぼ公式サンプル

expressを追加

$ npm i express
server.js
const fs = require('fs');
const { startServer, loadConfiguration } = require('snowpack');
const app = require('express')();
const React = require('react');
const ReactDOMServer = require('react-dom');

async function main() {
  const config = await loadConfiguration({}, './snowpack.config.js');
  const server = await startServer({ config, lockfile: null });
  const runtime = server.getServerRuntime();

  app.use(async (req, res, next) => {
    const importedComponent = await runtime.importModule('/dist/App.js');
    const MyReactComponent = importedComponent.exports.default;
    const html = ReactDOMServer.renderToString(
      React.createElement(MyReactComponent, null),
    );
    const htmlFile = fs.readFileSync('./index.html', 'utf8');
    const document = htmlFile.replace(
      /<div id="app"><\/div>/,
      `<div id="app">${html}</div>`,
    );

    res.send(document);
  });

  app.listen('3000', () => {
    console.log('http://localhost:3000');
  });
}

main();
uuyu-guuyu-g

とりあえず動いた

const { startServer, loadConfiguration } = require('snowpack');
const app = require('express')();
const React = require('react');
const ReactDOMServer = require('react-dom/server');

async function setupSnowpack() {
  const config = await loadConfiguration(
    {
      devOptions: {
        // expressをlocalhost:3000で使用しているので
        // 起動時snowpackのdev serverを開かない
        open: 'none',
        // SSRしようとすると react-refresh がないよと怒られ, hmrをオフにすると動いた
        hmr: false,
      },
    },
    './snowpack.config.js',
  );
  const server = await startServer({ config, lockfile: null });
  const runtime = server.getServerRuntime();

  return { runtime, server };
}

async function main() {
  const { runtime, server } = await setupSnowpack();

  app.use(async (req, res) => {
    const importedComponent = await runtime.importModule('/dist/Hello.js');
    const MyReactComponent = importedComponent.exports.default;
    const html = ReactDOMServer.renderToString(
      React.createElement(MyReactComponent, null),
    );
    const { contents } = await server.loadUrl('/index.html');
    const htmlFile = Buffer.from(contents).toString('utf8');
    const document = htmlFile.replace(
      /<div id="root"><\/div>/,
      `<div id="root">${html}</div>`,
    );

    res.send(document);
  });

  app.listen(3000, () => {
    console.log('http://localhost:3000');
  });
}

main();

動いてるけどこれなにしてるんだっけ?...

uuyu-guuyu-g

createConfiguration()の返り値

{
  root: 'path/to/repo',
  plugins: [
    {
      name: '@snowpack/plugin-esbuild',
      resolve: [Object],
      load: [AsyncFunction: load],
      cleanup: [Function: cleanup]
    }
  ],
  alias: {},
  exclude: [
    '**/node_modules/**/*',
    'path/to/repo/build/**/*'
  ],
  routes: [],
  devOptions: {
    secure: false,
    hostname: 'localhost',
    port: 8080,
    open: 'default',
    output: 'dashboard',
    hmrDelay: 0,
    hmrPort: undefined,
    hmrErrorOverlay: true
  },
  buildOptions: {
    out: 'path/to/repo/build',
    baseUrl: '/',
    metaUrlPath: '/_snowpack',
    clean: true,
    sourcemap: false,
    watch: false,
    htmlFragments: false,
    ssr: false
  },
  testOptions: { files: [ '__tests__/**/*', '**/*.@(spec|test).*' ] },
  packageOptions: {
    source: 'local',
    external: [],
    packageLookupFields: [],
    knownEntrypoints: [],
    rollup: { plugins: [] }
  },
  mount: {
    'path/to/repo': { url: '/', static: false, resolve: true }
  },
  optimize: undefined,
  _extensionMap: {
    '.mjs': [ '.js' ],
    '.jsx': [ '.js' ],
    '.ts': [ '.js' ],
    '.tsx': [ '.js' ]
  }
}
uuyu-guuyu-g

最終的には簡易的なstorybookを作りたい。loocみたいな。
snowpackの新しいapi使ってうまくいきそうな気がするんだけど

uuyu-guuyu-g
  const importModule = await runtime.importModule('/util.js');
  const exports = importModule.exports;

exports でexportされてるものの名前がわからないとアクセスできない。

default export ある意味名前が決まってるからいいんだけど、名前付きエクスポートはOnject.keysやらなんやらやってもアクセスできない。
debuggerで見るとgetterになってるのよなぁ。
あるファイルがエクスポートしているものをすべて取得したいってのができなさそう。