Skip to content
This repository has been archived by the owner on Aug 16, 2022. It is now read-only.

Kickoff: redesign #28

Closed
yyx990803 opened this issue Sep 19, 2017 · 17 comments
Closed

Kickoff: redesign #28

yyx990803 opened this issue Sep 19, 2017 · 17 comments

Comments

@yyx990803
Copy link
Member

yyx990803 commented Sep 19, 2017

So far, we have seen the logic of compiling an SFC duplicated in multiple projects:

  • vue-loader
  • vueify
  • rollup-plugin-vue
  • jest-vue

...and others. The goal here is to extract shared logic regarding SFC compilation into a dedicated package to avoid redundancy and ensure all of these tools are on the same page regarding SFC features and behavior.

Some initial thoughts. APIs should include:

  • SFC -> descriptor parsing
  • ? how to handle pre-processors vs. webpack loader chaining
  • Template -> render fn compilation
  • CSS handling (scoped CSS, postcss plugins, CSS modules)

Not entirely clear how much we should and could extract from vue-loader, as a lot of it is tightly coupled to webpack, but let's discuss.

/cc @znck @eddyerburgh @phanan @vuejs/collaborators

@Kingwl
Copy link
Member

Kingwl commented Sep 19, 2017

We should careful to add new API to avoid making Vue overcomplicated

@znck
Copy link
Member

znck commented Sep 19, 2017

how to handle pre-processors vs. webpack loader chaining

A consolidate like library for style.
Challenges: dependency resolution

@znck
Copy link
Member

znck commented Sep 20, 2017

/ping @eddyerburgh

@eddyerburgh
Copy link
Member

eddyerburgh commented Sep 20, 2017

I'm not sure how it would work with vue-loader.

But for jest-vue passing the compiled script, template HTML and styles would be ideal. We'd also need to pass SourceMaps.

style = {
  code: string,
  map: SourceMap
}

style = {
  style: string,
  scoped: Boolean
}

vueComponentCompiler.compile(string, script, style[])

The compiler would return the code and a sourcemap:

{
  code: string,
  map: SourceMap
}

@znck
Copy link
Member

znck commented Sep 21, 2017

SFC can have custom blocks too.

@znck
Copy link
Member

znck commented Sep 21, 2017

Proposed API.

interface Compiler {
  // Complete compiler. Takes in SFC.
  compile(source: String,   filename: String, options: CompilerOptions): Output
  // SFC to descriptor parsing.
  _parse(source: String,    filename: String, options: ParserOptions): Output
  // Consolidated style processor.
  _style(source: String,    filename: String, options: StyleProcessorOptions): CompiledStyle
  // Template to render function compiler (wrapper around vue-template-compiler).
  _template(source: String, filename: String, options: TemplateCompilerOptions): CompiledCode
}

type Output = {
  script: CompiledCode
  styles: Array<CompiledStyle>
} & {
  [customBlock: string]: Array<CompiledCode>
}

type CompiledCode = {
  code: string
  map: SourceMap
}

type CompiledStyle = CompiledCode & {
  module: { [key: string]: string }
}

type CompilerOptions = {}
type ParserOptions = {}
type TemplateCompilerOptions = {
  lang: string
  scopeId: string | null
}
type StyleProcessorOptions = {
  lang: string
  module: boolean | string
  scoped: boolean
  scopeId: string | null
}

type SourceMap = {}

@octref
Copy link
Member

octref commented Sep 21, 2017

Related: vuejs/vetur#276 vuejs/vetur#380

In Vetur we are looking into ways to parse Vue SFC and generate a descriptor that can be used for enhancing IntelliSense in <template>. For example, suggesting custom components when typing <, suggesting their attributes in <my-comp :, and in the future, suggesting methods / computed / etc in the interpolation.

Currently Vetur has limited support for libraries such as element-ui. And it depends on manually entered descriptor:

{
  "gutter": {"description": "grid spacing"},
  "justify": {"options": ["start", "end", "center", "space-around", "space-between"], "description": "horizontal alignment of flex layout"},
  "tag": {"description": "custom element tag"}
}

Would be intersting to keep Vue Language Server in mind while designing the compiler. That's the approach TS designed its compiler and language server to get great editing experience.

We probably need more momentum to introduce this to Vue community as whole, not only vetur users.
-- @HerringtonDarkholme

Now here we are :D

@rigor789
Copy link

We can currently add custom blocks to a SFC, but we would like to add a custom attributes to the <style> block for nativescript-vue so we can then easily extract styles specific to a given platform. The idea we have is something like:

<template></template>

<script></script>

<style></style> <!-- common styles shared acrosss platforms -->

<style platform="android"></style> <!-- styles only used on android -->

<style platform="ios"></style> <!-- styles only used on ios-->

Ideally this would be reusable for all blocks, so we could add the platform attribute to the <template> or <script> blocks as well.

@znck
Copy link
Member

znck commented Sep 26, 2017

Any attribute is supported on <style> tag, but build tool should use it to process styles selectively.

const compiler = require('vue-template-compiler')
const fs = require('fs')
const result = compiler.parseComponent(fs.readFileSync('./Test.vue').toString())

console.log(JSON.stringify(result, null, 2))

Descriptor for <style> block.

...
{
      "type": "style",
      "content": "",
      "start": 341,
      "attrs": {
        "platform": "ios",
        "module": true
      },
      "module": true,
      "end": 341
    }

P.S.: We should consider platform if consolidated style processing goes in vue-component-compiler.

@znck
Copy link
Member

znck commented Sep 26, 2017

Open questions:

  • How to handle pre-processors vs. webpack loader chaining?
  • CSS pre-processor consolidation vs. any other approach?

/cc @eddyerburgh @chrisvfritz @yyx990803

@HerringtonDarkholme
Copy link
Member

We probably need to first decide what features should be supported. A general pattern is that the more features are supported, the more assumptions are quired.

Several thoughts:


  • Feature: SFC -> descriptor parsing
  • Assumption: None!

This is the easiest and most universal feature. We can extract descriptor parsing from Vue source.


  • Feature: transpile
  • (Minimal) Assumption: we can parse source and resolve

Transpilation is easy for some language like CoffeeScript: one script in one script out. Yet most styling languages like sass and stylus needs whole file dependency. Say, component.styl depends on base.styl. We need resolve that dependency. Luckily, most transpilers provide API for transpile one file. Vue-component-compiler might need to mock a filename/file path for transpilers to resolve dependency. (Real file system access is probably necessary.)

That said, integrating a series of transpilers isn't easy.


  • Feature: Transpiler Configuration (.babelrc, tsconfig.json, .postcssrc, etc.)
  • Assumption: We can find the correct configuration and project root.

Finding config shouldn't be hard. (Notwithstanding details matter)


  • Feature: incremental compilation
  • Assumption: We need to tell dependency to external tools.

This is mainly for vue-loader. If we transpile source by vue-component-compiler, we need to inform webpack which files are used. Similar situation is expected for future tools like vuec, if we want to ship a command line vue SFC compiler.


  • Feature: error handling
  • Assumption: We need to robust compilers for the underlying languages.

Most compilers are bad at error handling. e.g Stylus cannot report the error line when a syntax happens. Pug compiler cannot recover from malformed input. This prevents component-compiler from being used in interactive environment like editor. @octref


It seems that the hard story begins from preprocessor support.

@yyx990803
Copy link
Member Author

yyx990803 commented Sep 29, 2017

Regarding pre-processors:

It should be noted that in vue-loader, pre-processing is delegated to other loaders. So for vue-template-compiler, we need to design it in a way so that pre-processing of each part is decoupled from the parse and re-assemble phases.

Basically, this means instead of providing one single API, we should first design several low-level APIs and then compose them into higher level usage:

// All usage hypothetical. We will likely need to pass additional options along the way
import { parse, compileTemplate, compileStyles, assemble } from 'vue-component-compiler'

// 1. parse
// equivalent: vue-loader/lib/parser.js
const descriptor = parse(source)

// 2. pre-processors
// (to be implemented by other tools)
// in vue-loader, this would be equivalent to `getRequireString`
const processedStyles = preProcessStyles(descriptor.styles)
const processedScript = preProcessScript(descriptor.script)
const processedTemplate = preProcessTemplate(descriptor.template)

// 3. vue-specific post processing
// equivalent: vue-loader/lib/style-compiler
const finalStyles = compileStyles(processedStyles)
// equivalent: vue-loader/lib/template-compiler
const renderFns = compileTemplate(processedTemplate)

// 4. assemble final component
// equivalent: parts of vue-loader/lib/loader.js and vue-loader/component-normalizer.js
const finalComponentCode = assemble(processedScript, renderFns, finalStyles)

Remember the primary goal is to extract tooling agnostic logic and reduce redundancy instead of providing a standalone compiler.

@HerringtonDarkholme
Copy link
Member

Indeed, providing utility function looks better. It imposes least assumption and thus make library use at free.

@znck
Copy link
Member

znck commented Sep 29, 2017

Looks clean.
Additionally

  • Descriptors should include file path ( preprocessor would need it resolve relative dependencies ) build tool (e.g. vue-loader) is already aware of file path.

@gzzhanghao
Copy link

How about providing a standalone compiler with options to customize pre-process and assemble steps?
I'm using vue-compiler in my project and I think it can be adapted for vueify with very few changes.

@znck
Copy link
Member

znck commented Nov 27, 2017

The primary goal here is to extract tooling agnostic logic and reduce redundancy instead of providing a standalone compiler.

@yyx990803
Copy link
Member Author

Closing in favor of #34 .

@yyx990803 yyx990803 mentioned this issue Dec 16, 2017
13 tasks
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

8 participants