summaryrefslogtreecommitdiff
path: root/src/dev.ts
blob: 49faafd40bc08d4209a70235dbf190bc7a18fe80 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import type { AstroConfig } from './@types/astro';
import { loadConfiguration, startServer as startSnowpackServer } from 'snowpack';
import { existsSync, promises as fsPromises } from 'fs';
import http from 'http';
import { createRequire } from 'module';

const { readFile } = fsPromises;

const require = createRequire(import.meta.url);

const hostname = '127.0.0.1';
const port = 3000;

export default async function (astroConfig: AstroConfig) {
  const { projectRoot, hmxRoot } = astroConfig;

  const internalPath = new URL('./frontend/', import.meta.url);
  const snowpackConfigPath = new URL('./snowpack.config.js', projectRoot);

  // Workaround for SKY-251
  const hmxPlugOptions: { resolve?: (s: string) => string } = {};
  if (existsSync(new URL('./package-lock.json', projectRoot))) {
    const pkgLockStr = await readFile(new URL('./package-lock.json', projectRoot), 'utf-8');
    const pkgLock = JSON.parse(pkgLockStr);
    hmxPlugOptions.resolve = (pkgName: string) => {
      const ver = pkgLock.dependencies[pkgName].version;
      return `/_snowpack/pkg/${pkgName}.v${ver}.js`;
    };
  }

  const snowpackConfig = await loadConfiguration(
    {
      root: projectRoot.pathname,
      mount: {
        [hmxRoot.pathname]: '/_hmx',
        [internalPath.pathname]: '/__hmx_internal__',
      },
      plugins: [['astro/snowpack-plugin', hmxPlugOptions]],
      devOptions: {
        open: 'none',
        output: 'stream',
        port: 0,
      },
      packageOptions: {
        knownEntrypoints: ['preact-render-to-string'],
        external: ['@vue/server-renderer'],
      },
    },
    snowpackConfigPath.pathname
  );
  const snowpack = await startSnowpackServer({
    config: snowpackConfig,
    lockfile: null,
  });
  const runtime = snowpack.getServerRuntime();

  const server = http.createServer(async (req, res) => {
    const fullurl = new URL(req.url || '/', 'https://example.org/');
    const reqPath = decodeURI(fullurl.pathname);
    const selectedPage = reqPath.substr(1) || 'index';
    console.log(reqPath, selectedPage);

    const selectedPageLoc = new URL(`./pages/${selectedPage}.hmx`, hmxRoot);
    const selectedPageMdLoc = new URL(`./pages/${selectedPage}.md`, hmxRoot);
    const selectedPageUrl = `/_hmx/pages/${selectedPage}.js`;

    // Non-hmx pages
    if (!existsSync(selectedPageLoc) && !existsSync(selectedPageMdLoc)) {
      try {
        const result = await snowpack.loadUrl(reqPath);
        if (result.contentType) {
          res.setHeader('Content-Type', result.contentType);
        }
        res.write(result.contents);
        res.end();
      } catch (err) {
        console.log('Not found', reqPath);
        res.statusCode = 404;
        res.setHeader('Content-Type', 'text/plain');
        res.end('Not Found');
      }
      return;
    }

    try {
      const mod = await runtime.importModule(selectedPageUrl);
      const html = await mod.exports.default();
      res.statusCode = 200;
      res.setHeader('Content-Type', 'text/html; charset=utf-8');
      res.end(html);
    } catch (err) {
      console.log(err);
    }
  });

  server.listen(port, hostname, () => {
    console.log(`Server running at http://${hostname}:${port}/`);
  });
}