Repository: vuejs/vue
Pull request: Add types for vue-server-renderer (fix #5772)

<!--
Please make sure to read the Pull Request Guidelines:
https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#pull-request-guidelines
-->

<!-- PULL REQUEST TEMPLATE -->
<!-- (Update "[ ]" to "[x]" to check a box) -->

**What kind of change does this PR introduce?** (check at least one)

- [x] Other, please describe: Add type declarations

**Does this PR introduce a breaking change?** (check one)

- [x] No

**The PR fulfills these requirements:**

- [x] It's submitted to the `dev` branch for v2.x (or to a previous version branch), _not_ the `master` branch
- [x] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number)
- [x] All tests are passing: https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#development-setup
- [x] New/updated tests are included

**Other information:**
Added the type declaration file of vue-server-renderer into its `package` directory. Also added a brief test code of the declaration.

Changed files:
filename: packages/vue-server-renderer/package.json
patch: @@ -3,6 +3,7 @@
   "version": "2.3.3",
   "description": "server renderer for Vue 2.0",
   "main": "index.js",
+  "types": "types/index.d.ts",
   "repository": {
     "type": "git",
     "url": "git+https://github.com/vuejs/vue.git"
@@ -17,6 +18,9 @@
   "bugs": {
     "url": "https://github.com/vuejs/vue/issues"
   },
+  "scripts": {
+    "test:types": "tsc -p types"
+  },
   "dependencies": {
     "chalk": "^1.1.3",
     "hash-sum": "^1.0.2",
@@ -27,5 +31,10 @@
     "source-map": "0.5.6",
     "serialize-javascript": "^1.3.0"
   },
+  "devDependencies": {
+    "@types/node": "^7.0.22",
+    "typescript": "^2.3.3",
+    "vue": "file:../.."
+  },
   "homepage": "https://github.com/vuejs/vue/tree/dev/packages/vue-server-renderer#readme"
 }

filename: packages/vue-server-renderer/types/index.d.ts
patch: @@ -0,0 +1,44 @@
+import Vue = require('vue');
+import { Readable } from 'stream';
+
+export declare function createRenderer(options?: RendererOptions): Renderer;
+
+export declare function createBundleRenderer(bundle: string | object, options?: BundleRendererOptions): BundleRenderer;
+
+type RenderCallback = (err: Error | null, html: string) => void;
+
+interface Renderer {
+  renderToString(vm: Vue, callback: RenderCallback): void;
+  renderToString(vm: Vue, context: object, callback: RenderCallback): void;
+
+  renderToStream(vm: Vue, context?: object): Readable;
+}
+
+interface BundleRenderer {
+  renderToString(callback: RenderCallback): void;
+  renderToString(context: object, callback: RenderCallback): void;
+
+  renderToStream(context?: object): Readable;
+}
+
+interface RendererOptions {
+  template?: string;
+  inject?: boolean;
+  shouldPreload?: (file: string, type: string) => boolean;
+  cache?: RenderCache;
+  directives?: {
+    [key: string]: (vnode: Vue.VNode, dir: Vue.VNodeDirective) => void
+  };
+}
+
+interface BundleRendererOptions extends RendererOptions {
+  clientManifest?: object;
+  runInNewContext?: boolean | 'once';
+  basedir?: string;
+}
+
+interface RenderCache {
+  get: (key: string, cb?: (res: string) => void) => string | void;
+  set: (key: string, val: string) => void;
+  has?: (key: string, cb?: (hit: boolean) => void) => boolean | void;
+}

filename: packages/vue-server-renderer/types/test.ts
patch: @@ -0,0 +1,84 @@
+import Vue = require('vue');
+import { readFileSync } from 'fs';
+import { createRenderer, createBundleRenderer } from '../';
+
+function createApp (context: any) {
+  return new Vue({
+    data: {
+      url: context.url
+    },
+    template: `<div>The visited URL is: {{ url }}</div>`
+  });
+}
+
+// Renderer test
+const app = createApp({ url: 'http://localhost:8000/' });
+
+const renderer = createRenderer({
+  template: readFileSync('./index.template.html', 'utf-8')
+});
+
+const context = {
+  title: 'Hello',
+  meta: `
+    <meta name="description" content="Vue.js SSR Example">
+  `
+};
+
+renderer.renderToString(app, context, (err, html) => {
+  if (err) throw err;
+  const res: string = html;
+});
+
+renderer.renderToStream(app, context).on('data', chunk => {
+  const html = chunk.toString();
+});
+
+// Bundle renderer test
+declare const cacheClient: { [key: string]: string };
+
+const bundleRenderer = createBundleRenderer('/path/to/vue-ssr-server-bundle.json', {
+  inject: false,
+  runInNewContext: 'once',
+  basedir: '/path/to/base',
+
+  shouldPreload: (file, type) => {
+    if (type === 'script' || type === 'style') {
+      return true;
+    }
+    if (type === 'font') {
+      return /\.woff2$/.test(file);
+    }
+    if (type === 'image') {
+      return file === 'hero.jpg';
+    }
+    return false;
+  },
+
+  cache: {
+    get: key => {
+      return cacheClient[key];
+    },
+    set: (key, val) => {
+      cacheClient[key] = val;
+    },
+    has: key => {
+      return !!cacheClient[key];
+    }
+  },
+
+  directives: {
+    example (vnode: Vue.VNode, directiveMeta: Vue.VNodeDirective) {
+      // transform vnode based on directive binding metadata
+    }
+  }
+});
+
+bundleRenderer.renderToString(context, (err, html) => {
+  if (err) throw err;
+  const res: string = html;
+});
+
+bundleRenderer.renderToStream(context).on('data', chunk => {
+  const html = chunk.toString();
+});

filename: packages/vue-server-renderer/types/tsconfig.json
patch: @@ -0,0 +1,13 @@
+{
+  "compilerOptions": {
+    "target": "es5",
+    "module": "commonjs",
+    "moduleResolution": "node",
+    "strict": true,
+    "noEmit": true
+  },
+  "compileOnSave": false,
+  "include": [
+    "**/*.ts"
+  ]
+}