Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clover] Proposal: transferrable assets #2132

Closed
lacolaco opened this issue May 26, 2021 · 7 comments · Fixed by #2141
Closed

[clover] Proposal: transferrable assets #2132

lacolaco opened this issue May 26, 2021 · 7 comments · Fixed by #2141

Comments

@lacolaco
Copy link
Contributor

lacolaco commented May 26, 2021

Proposal

What is the summary of the proposal?

This proposal adds a capability for static rendering with external content files.

Usecases

  • Blog site
  • Documentation
    • App Shell + Markdown contents (like AIO)
  • Load initial data from JSON and fetch fresh one.

What is the proposal?

Builder-side

Adding a transferAssets option to the @nguniversal/builders:static-generator, which is an array that accepts paths of assets. It is relative from the output path of the browser target.

       "build-static": {
          "builder": "@nguniversal/builders:static-generator",
          "options": {
            "browserTarget": "clover:build:production",
            "routes": [
              "/",
              "pokemon/pikachu",
              "pokemon/charmander",
              "pokemon/squirtle",
              "pokemon/bulbasaur"
            ],
            "transferAssets": [
              "pokemon.json"
            ]
          }
        }

Engine-side

Embedding assets as a JSON state immediately after ngRenderMode has been built. TransferState will be initialized with the state.

      // Embed assets within transfer state
      if (options.transferAssetsPaths) {
        const assets = await Promise.all(
          options.transferAssetsPaths.map((assetPath) => ({
            assetPath,
            content: fs.promises.readFile(path.resolve(options.publicPath, assetPath), 'utf-8'),
          })),
        );
        const assetMap = assets.reduce(
          (map, asset) => ({ ...map, [asset.assetPath]: asset.content }),
          {},
        );
        const state = JSON.stringify(assetMap);
        this.embedTransferState(doc, ngRenderMode.appId, state);
      }

      await ngRenderMode.getWhenStable();

This is an implementation for proof of concept. I don't think it is production-ready but enough to explain what this proposal aims for.

lacolaco@52f7b7d

Is there anything else we should know?

Considerations

  • jsdom doesn't support intercepting to XHR, so CustomResourceLoader cannot help this usecase.
  • At first, I thought assets from the browser target can be used for this purpose, but assets can contain non-text files like images. So explicit option would be needed.
@alan-agius4
Copy link
Collaborator

Hi @lacolaco,

Thanks for this request but I am not understanding the use-case of this.

We already provide a way to intercept and cache all HTTP requests, using the TransferHttpCacheModule.

@alan-agius4 alan-agius4 added the need: more info Reporter must clarify the issue label May 26, 2021
@lacolaco
Copy link
Contributor Author

lacolaco commented May 26, 2021

@alan-agius4 Yes TransferHttpCacheModule can, only if the request is fully external. In other words, an HTTP request while rendering mode fails when its URL is relative because no local server is running.

This is a similar approach to file-loader of webpack. I'd like to inject local external file contents into the app at the build time.

@alan-agius4
Copy link
Collaborator

I now see what you mean.

Ideally, we should do this without extending the API, we already intercept the all HTTP request in TransferHttpCacheModule.

I think in this case what we can do is add a fallback handler that adds a file:// to requests. XHR can work with local file as well as remote files. That being said, this would need to be tested thoroughly since it can expose a security risk.

JFYI: in the code provided above, the assets are actually stored in that state but are never used by the client, this is because the cache key that the HTTP interceptor uses is more complex

const storeKey = this.makeCacheKey(req.method, req.url, req.params);

@alan-agius4 alan-agius4 added type: feature area: common and removed need: more info Reporter must clarify the issue labels May 26, 2021
@lacolaco
Copy link
Contributor Author

lacolaco commented May 26, 2021

@alan-agius4 That sounds the best. The difficulty is that not all requests should be fallback to file:// , and the outputPath of the browser target needs to be communicated to the Interceptor.

@lacolaco
Copy link
Contributor Author

I did some research on integration with msw to see if it might be useful. It may be able to solve the problem by actually creating a server instance of Node.js.

@alan-agius4
Copy link
Collaborator

Yeah, spawning a server is also another very valid approach.

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Jul 2, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants