Repository: motion-canvas/motion-canvas
File: packages/vite-plugin/src/partials/editor.ts (TypeScript)

Code before:
  const editorFile = fs.readFileSync(path.resolve(editorPath, 'editor.html'));
  const htmlParts = editorFile
    .toString()
    .replace('{{style}}', `/@fs/${path.resolve(editorPath, 'style.css')}`)
    .split('{{source}}');
  const createHtml = (src: string) => htmlParts[0] + src + htmlParts[1];
  const resolvedEditorId = '\0virtual:editor';

  const lookup = new Map<string, ProjectData>();
  for (const project of projects) {
    lookup.set(project.filePath, project);
  }

  let query: string;

  return {
    name: 'motion-canvas:editor',

    async resolveId(source) {
      if (source.startsWith(resolvedEditorId)) {
        const [id, projectQuery] = source.split('?');
        query = projectQuery;
        return id;
      }
      return null;
    },

    async load(id) {
      if (id.startsWith(resolvedEditorId)) {
        if (projects.length === 1) {
          /* language=typescript */
          return `\
import {editor} from '${editor}';
import project from '${projects[0].url}?project';
editor(project);
`;
        }

        if (query) {
          const params = new URLSearchParams(query);
          const name = params.get('project');
          if (name && lookup.has(name)) {
            /* language=typescript */
            return `\
import {editor} from '${editor}';
import project from '${lookup.get(name)!.url}?project';
editor(project);
`;
          }
        }


Review comment (security):
This is a bit too hacky. There's no guarantee the corresponding `load` will be called right after `resolveId`. The same server may be used to serve two projects at the same time, for example.
Also, this global `query` variable will break the project selections screen:
1. Visit localhost:9000
2. Choose one of the projects
3. Go back to localhost:9000
4. Instead of the selection screen, the previously chosen project is immediately loaded

The reason you were getting an error was because the name was not escaped when generating the `virtual:editor?project=[name]` url. All you need to do is add `encodeURIComponent` in the server a few lines below:
```ts
if (name && lookup.has(name)) {
  res.setHeader('Content-Type', 'text/html');
  res.end(
    createHtml(
      // right here:
      `/@id/__x00__virtual:editor?project=${encodeURIComponent(
        name,
      )}`,
    ),
  );
  return;
}
```
and it will work just fine without `resolveId` and the global `query` variable.

Code after:
  const editorFile = fs.readFileSync(path.resolve(editorPath, 'editor.html'));
  const htmlParts = editorFile
    .toString()
    .replace('{{style}}', `/@fs/${path.resolve(editorPath, 'style.css')}`)
    .split('{{source}}');
  const createHtml = (src: string) => htmlParts[0] + src + htmlParts[1];
  const resolvedEditorId = '\0virtual:editor';

  const lookup = new Map<string, ProjectData>();
  for (const project of projects) {
    lookup.set(project.url, project);
  }

  return {
    name: 'motion-canvas:editor',

    async load(id) {
      const [, query] = id.split('?');

      if (id.startsWith(resolvedEditorId)) {
        if (projects.length === 1) {
          /* language=typescript */
          return `\
import {editor} from '${editor}';
import project from '${projects[0].filePath}?project';
editor(project);
`;
        }

        if (query) {
          const params = new URLSearchParams(query);
          const name = params.get('project');
          if (name && lookup.has(name)) {
            /* language=typescript */
            return `\
import {editor} from '${editor}';
import project from '${lookup.get(name)!.filePath}?project';
editor(project);
`;
          }
        }

        /* language=typescript */
        return `\
import {index} from '${editor}';
index(${JSON.stringify(projects)});
`;
      }
    },

    configureServer(server) {