diff --git a/.gitignore b/.gitignore
index df5f1e83..c0b8ab88 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,9 +17,11 @@ jspm_packages
# Optional npm cache directory
.npm
+package-lock.json
# Optional REPL history
.node_repl_history
-# IDEA directory
+# Editor directories and files
.idea
+.vscode
\ No newline at end of file
diff --git a/README.md b/README.md
index e7aaf852..18dfc1c3 100644
--- a/README.md
+++ b/README.md
@@ -104,6 +104,10 @@ should keep free 1 core for *build* and 1 core for a *system* *(for example syst
node doesn't share memory between workers - keep in mind that memory usage will increase. Be aware that in some scenarios increasing workers
number **can increase checking time**. Default: `ForkTsCheckerWebpackPlugin.ONE_CPU`.
+* **vue** `boolean`:
+If `true`, the linter and compiler will process VueJs single-file-component (.vue) files. See the
+[Vue section](https://github.com/Realytics/fork-ts-checker-webpack-plugin#vue) further down for information on how to correctly setup your project.
+
### Pre-computed consts:
* `ForkTsCheckerWebpackPlugin.ONE_CPU` - always use one CPU
* `ForkTsCheckerWebpackPlugin.ALL_CPUS` - always use all CPUs (will increase build time)
@@ -141,5 +145,94 @@ This plugin provides some custom webpack hooks (all are sync):
|`fork-ts-checker-emit`| Service will add errors and warnings to webpack compilation ('build' mode) | `diagnostics`, `lints`, `elapsed` |
|`fork-ts-checker-done`| Service finished type checking and webpack finished compilation ('watch' mode) | `diagnostics`, `lints`, `elapsed` |
+## Vue
+1. Turn on the vue option in the plugin in your webpack config:
+
+```
+ new ForkTsCheckerWebpackPlugin({
+ tslint: true,
+ vue: true
+ })
+```
+
+2. To activate TypeScript in your `.vue` files, you need to ensure your script tag's language attribute is set
+to `ts` or `tsx` (also make sure you include the `.vue` extension in all your import statements as shown below):
+
+```html
+
+```
+
+3. Ideally you are also using `ts-loader` (in transpileOnly mode). Your Webpack config rules may look something like this:
+
+```
+{
+ test: /\.ts$/,
+ loader: 'ts-loader',
+ include: [resolve('src'), resolve('test')],
+ options: {
+ appendTsSuffixTo: [/\.vue$/],
+ transpileOnly: true
+ }
+},
+{
+ test: /\.vue$/,
+ loader: 'vue-loader',
+ options: vueLoaderConfig
+},
+```
+4. Add rules to your `tslint.json` and they will be applied to Vue files. For example, you could apply the Standard JS rules [tslint-config-standard](https://github.com/blakeembrey/tslint-config-standard) like this:
+
+```json
+{
+ "defaultSeverity": "error",
+ "extends": [
+ "tslint-config-standard"
+ ]
+}
+```
+5. Ensure your `tsconfig.json` includes .vue files:
+
+```
+// tsconfig.json
+{
+ "include": [
+ "src/**/*.ts",
+ "src/**/*.vue"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+}
+```
+
+6. The commonly used `@` path wildcard will work if you set up a `baseUrl` and `paths` (in `compilerOptions`) to include `@/*`. If you don't set this, then
+the fallback for the `@` wildcard will be `[tsconfig directory]/src` (we hope to make this more flexible on future releases):
+```
+// tsconfig.json
+{
+ "compilerOptions": {
+
+ // ...
+
+ "baseUrl": ".",
+ "paths": {
+ "@/*": [
+ "src/*"
+ ]
+ }
+ }
+}
+
+// In a .ts or .vue file...
+import Hello from '@/components/hello.vue'
+```
+
+7. If you are working in **VSCode**, you can get extensions [Vetur](https://marketplace.visualstudio.com/items?itemName=octref.vetur) and [TSLint Vue](https://marketplace.visualstudio.com/items?itemName=prograhammer.tslint-vue) to complete the developer workflow.
+
## License
MIT
diff --git a/package.json b/package.json
index e7d6a853..2465dde2 100644
--- a/package.json
+++ b/package.json
@@ -50,8 +50,10 @@
"@types/lodash.startswith": "^4.2.3",
"@types/minimatch": "^3.0.1",
"@types/node": "^8.0.26",
+ "@types/resolve": "0.0.4",
"@types/webpack": "^3.0.10",
"chai": "^3.5.0",
+ "css-loader": "^0.28.7",
"eslint": "^3.19.0",
"istanbul": "^0.4.5",
"mocha": "^3.4.1",
@@ -62,6 +64,10 @@
"ts-loader": "^2.1.0",
"tslint": "^5.0.0",
"typescript": "^2.1.0",
+ "vue": "^2.5.9",
+ "vue-class-component": "^6.1.1",
+ "vue-loader": "^13.5.0",
+ "vue-template-compiler": "^2.5.9",
"webpack": "^3.0.0"
},
"peerDependencies": {
@@ -76,6 +82,8 @@
"lodash.isfunction": "^3.0.8",
"lodash.isstring": "^4.0.1",
"lodash.startswith": "^4.2.1",
- "minimatch": "^3.0.4"
+ "minimatch": "^3.0.4",
+ "resolve": "^1.5.0",
+ "vue-parser": "^1.1.5"
}
}
diff --git a/src/IncrementalChecker.ts b/src/IncrementalChecker.ts
index a66a67d7..56fdb704 100644
--- a/src/IncrementalChecker.ts
+++ b/src/IncrementalChecker.ts
@@ -9,6 +9,7 @@ import WorkSet = require('./WorkSet');
import NormalizedMessage = require('./NormalizedMessage');
import CancellationToken = require('./CancellationToken');
import minimatch = require('minimatch');
+import VueProgram = require('./VueProgram');
// Need some augmentation here - linterOptions.exclude is not (yet) part of the official
// types for tslint.
@@ -36,13 +37,16 @@ class IncrementalChecker {
programConfig: ts.ParsedCommandLine;
watcher: FilesWatcher;
+ vue: boolean;
+
constructor(
programConfigFile: string,
linterConfigFile: string | false,
watchPaths: string[],
workNumber: number,
workDivision: number,
- checkSyntacticErrors: boolean
+ checkSyntacticErrors: boolean,
+ vue: boolean
) {
this.programConfigFile = programConfigFile;
this.linterConfigFile = linterConfigFile;
@@ -50,6 +54,7 @@ class IncrementalChecker {
this.workNumber = workNumber || 0;
this.workDivision = workDivision || 1;
this.checkSyntacticErrors = checkSyntacticErrors || false;
+ this.vue = vue || false;
// Use empty array of exclusions in general to avoid having
// to check of its existence later on.
this.linterExclusions = [];
@@ -130,7 +135,8 @@ class IncrementalChecker {
nextIteration() {
if (!this.watcher) {
- this.watcher = new FilesWatcher(this.watchPaths, ['.ts', '.tsx']);
+ const watchExtensions = this.vue ? ['.ts', '.tsx', '.vue'] : ['.ts', '.tsx'];
+ this.watcher = new FilesWatcher(this.watchPaths, watchExtensions);
// connect watcher with register
this.watcher.on('change', (filePath: string, stats: fs.Stats) => {
@@ -143,10 +149,6 @@ class IncrementalChecker {
this.watcher.watch();
}
- if (!this.programConfig) {
- this.programConfig = IncrementalChecker.loadProgramConfig(this.programConfigFile);
- }
-
if (!this.linterConfig && this.linterConfigFile) {
this.linterConfig = IncrementalChecker.loadLinterConfig(this.linterConfigFile);
@@ -158,12 +160,31 @@ class IncrementalChecker {
}
}
- this.program = IncrementalChecker.createProgram(this.programConfig, this.files, this.watcher, this.program);
+ this.program = this.vue ? this.loadVueProgram() : this.loadDefaultProgram();
+
if (this.linterConfig) {
this.linter = IncrementalChecker.createLinter(this.program);
}
}
+ loadVueProgram() {
+ this.programConfig = this.programConfig || VueProgram.loadProgramConfig(this.programConfigFile);
+
+ return VueProgram.createProgram(
+ this.programConfig,
+ path.dirname(this.programConfigFile),
+ this.files,
+ this.watcher,
+ this.program
+ );
+ }
+
+ loadDefaultProgram() {
+ this.programConfig = this.programConfig || IncrementalChecker.loadProgramConfig(this.programConfigFile);
+
+ return IncrementalChecker.createProgram(this.programConfig, this.files, this.watcher, this.program);
+ }
+
hasLinter() {
return this.linter !== undefined;
}
diff --git a/src/VueProgram.ts b/src/VueProgram.ts
new file mode 100644
index 00000000..57a2a47e
--- /dev/null
+++ b/src/VueProgram.ts
@@ -0,0 +1,145 @@
+import fs = require('fs');
+import path = require('path');
+import ts = require('typescript');
+import FilesRegister = require('./FilesRegister');
+import FilesWatcher = require('./FilesWatcher');
+import vueParser = require('vue-parser');
+
+class VueProgram {
+ static loadProgramConfig(configFile: string) {
+ const extraExtensions = ['vue'];
+
+ const parseConfigHost: ts.ParseConfigHost = {
+ fileExists: ts.sys.fileExists,
+ readFile: ts.sys.readFile,
+ useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames,
+ readDirectory: (rootDir, extensions, excludes, includes, depth) => {
+ return ts.sys.readDirectory(rootDir, extensions.concat(extraExtensions), excludes, includes, depth);
+ }
+ };
+
+ const parsed = ts.parseJsonConfigFileContent(
+ // Regardless of the setting in the tsconfig.json we want isolatedModules to be false
+ Object.assign(ts.readConfigFile(configFile, ts.sys.readFile).config, { isolatedModules: false }),
+ parseConfigHost,
+ path.dirname(configFile)
+ );
+
+ parsed.options.allowNonTsExtensions = true;
+
+ return parsed;
+ }
+
+ /**
+ * Since 99.9% of Vue projects use the wildcard '@/*', we only search for that in tsconfig CompilerOptions.paths.
+ * The path is resolved with thie given substitution and includes the CompilerOptions.baseUrl (if given).
+ * If no paths given in tsconfig, then the default substitution is '[tsconfig directory]/src'.
+ * (This is a fast, simplified inspiration of what's described here: https://github.com/Microsoft/TypeScript/issues/5039)
+ */
+ public static resolveNonTsModuleName(moduleName: string, containingFile: string, basedir: string, options: ts.CompilerOptions) {
+ const baseUrl = options.baseUrl ? options.baseUrl : basedir;
+ const pattern = options.paths ? options.paths['@/*'] : undefined;
+ const substitution = pattern ? options.paths['@/*'][0].replace('*', '') : 'src';
+ const isWildcard = moduleName.substr(0, 2) === '@/';
+ const isRelative = !path.isAbsolute(moduleName);
+
+ if (isWildcard) {
+ moduleName = path.resolve(baseUrl, substitution, moduleName.substr(2));
+ } else if (isRelative) {
+ moduleName = path.resolve(path.dirname(containingFile), moduleName);
+ }
+
+ return moduleName;
+ }
+
+ public static isVue(filePath: string) {
+ return path.extname(filePath) === '.vue';
+ }
+
+ static createProgram(
+ programConfig: ts.ParsedCommandLine,
+ basedir: string,
+ files: FilesRegister,
+ watcher: FilesWatcher,
+ oldProgram: ts.Program
+ ) {
+ const host = ts.createCompilerHost(programConfig.options);
+ const realGetSourceFile = host.getSourceFile;
+
+ // We need a host that can parse Vue SFCs (single file components).
+ host.getSourceFile = (filePath, languageVersion, onError) => {
+ // first check if watcher is watching file - if not - check it's mtime
+ if (!watcher.isWatchingFile(filePath)) {
+ try {
+ const stats = fs.statSync(filePath);
+
+ files.setMtime(filePath, stats.mtime.valueOf());
+ } catch (e) {
+ // probably file does not exists
+ files.remove(filePath);
+ }
+ }
+
+ // get source file only if there is no source in files register
+ if (!files.has(filePath) || !files.getData(filePath).source) {
+ files.mutateData(filePath, (data) => {
+ data.source = realGetSourceFile(filePath, languageVersion, onError);
+ });
+ }
+
+ let source = files.getData(filePath).source;
+
+ // get typescript contents from Vue file
+ if (source && VueProgram.isVue(filePath)) {
+ const parsed = vueParser.parse(source.text, 'script', { lang: ['ts', 'tsx', 'js', 'jsx'] });
+ source = ts.createSourceFile(filePath, parsed, languageVersion, true);
+ }
+
+ return source;
+ };
+
+ // We need a host with special module resolution for Vue files.
+ host.resolveModuleNames = (moduleNames, containingFile) => {
+ const resolvedModules: ts.ResolvedModule[] = [];
+
+ for (const moduleName of moduleNames) {
+ // Try to use standard resolution.
+ const result = ts.resolveModuleName(moduleName, containingFile, programConfig.options, {
+ fileExists: host.fileExists,
+ readFile: host.readFile
+ });
+
+ if (result.resolvedModule) {
+ resolvedModules.push(result.resolvedModule);
+ } else {
+ // For non-ts extensions.
+ const absolutePath = VueProgram.resolveNonTsModuleName(moduleName, containingFile, basedir, programConfig.options);
+
+ if (VueProgram.isVue(moduleName)) {
+ resolvedModules.push({
+ resolvedFileName: absolutePath,
+ extension: '.ts'
+ } as ts.ResolvedModuleFull);
+ } else {
+ resolvedModules.push({
+ // If the file does exist, return an empty string (because we assume user has provided a ".d.ts" file for it).
+ resolvedFileName: host.fileExists(absolutePath) ? '' : absolutePath,
+ extension: '.ts'
+ } as ts.ResolvedModuleFull);
+ }
+ }
+ }
+
+ return resolvedModules;
+ };
+
+ return ts.createProgram(
+ programConfig.fileNames,
+ programConfig.options,
+ host,
+ oldProgram // re-use old program
+ );
+ }
+}
+
+export = VueProgram;
diff --git a/src/index.ts b/src/index.ts
index f5c1d79f..dab256f9 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -30,6 +30,7 @@ interface Options {
checkSyntacticErrors: boolean;
memoryLimit: number;
workers: number;
+ vue: boolean;
}
/**
@@ -84,6 +85,8 @@ class ForkTsCheckerWebpackPlugin {
service: childProcess.ChildProcess;
+ vue: boolean;
+
constructor(options: Options) {
options = options || {} as Options;
this.options = Object.assign({}, options);
@@ -126,6 +129,8 @@ class ForkTsCheckerWebpackPlugin {
this.typescriptVersion = require('typescript').version;
this.tslintVersion = this.tslint ? require('tslint').Linter.VERSION : undefined;
+
+ this.vue = options.vue === true; // default false
}
static createFormatter(type: 'default' | 'codeframe', options: any) {
@@ -313,7 +318,8 @@ class ForkTsCheckerWebpackPlugin {
WATCH: this.isWatching ? this.watchPaths.join('|') : '',
WORK_DIVISION: Math.max(1, this.workersNumber),
MEMORY_LIMIT: this.memoryLimit,
- CHECK_SYNTACTIC_ERRORS: this.checkSyntacticErrors
+ CHECK_SYNTACTIC_ERRORS: this.checkSyntacticErrors,
+ VUE: this.vue
}
),
stdio: ['inherit', 'inherit', 'inherit', 'ipc']
diff --git a/src/service.ts b/src/service.ts
index 65b37ac9..7d60bd26 100644
--- a/src/service.ts
+++ b/src/service.ts
@@ -10,7 +10,8 @@ const checker = new IncrementalChecker(
process.env.WATCH === '' ? [] : process.env.WATCH.split('|'),
parseInt(process.env.WORK_NUMBER, 10),
parseInt(process.env.WORK_DIVISION, 10),
- process.env.CHECK_SYNTACTIC_ERRORS === 'true'
+ process.env.CHECK_SYNTACTIC_ERRORS === 'true',
+ process.env.VUE === 'true'
);
function run(cancellationToken: CancellationToken) {
diff --git a/test/integration/vue.spec.js b/test/integration/vue.spec.js
new file mode 100644
index 00000000..18eeced6
--- /dev/null
+++ b/test/integration/vue.spec.js
@@ -0,0 +1,150 @@
+
+var describe = require('mocha').describe;
+var it = require('mocha').it;
+var expect = require('chai').expect;
+var path = require('path');
+var webpack = require('webpack');
+var process = require('process');
+var ForkTsCheckerWebpackPlugin = require('../../lib/index');
+var IncrementalChecker = require('../../lib/IncrementalChecker');
+
+describe('[INTEGRATION] vue', function () {
+ this.timeout(30000);
+ process.setMaxListeners(20);
+ var plugin;
+ var files;
+ var compiler;
+ var checker;
+
+ function createCompiler(options) {
+ plugin = new ForkTsCheckerWebpackPlugin(Object.assign({}, options, { silent: true }));
+
+ compiler = webpack({
+ context: path.resolve(__dirname, './vue'),
+ entry: './src/index.ts',
+ output: {
+ path: path.resolve(__dirname, '../../tmp')
+ },
+ resolve: {
+ extensions: ['.ts', '.js', '.vue', '.json'],
+ alias: {
+ '@': path.resolve(__dirname, './vue/src'),
+ }
+ },
+ module: {
+ rules: [
+ {
+ test: /\.vue$/,
+ loader: 'vue-loader'
+ },
+ {
+ test: /\.ts$/,
+ loader: 'ts-loader',
+ options: {
+ appendTsSuffixTo: [/\.vue$/],
+ transpileOnly: true,
+ silent: true
+ }
+ },
+ {
+ test: /\.css$/,
+ loader: 'css-loader'
+ }
+ ]
+ },
+ plugins: [
+ plugin
+ ]
+ });
+
+ files = {
+ 'example.vue': path.resolve(compiler.context, 'src/example.vue'),
+ 'syntacticError.ts': path.resolve(compiler.context, 'src/syntacticError.ts')
+ };
+
+ checker = new IncrementalChecker(
+ plugin.tsconfigPath,
+ plugin.tslintPath || false,
+ [compiler.context],
+ ForkTsCheckerWebpackPlugin.ONE_CPU,
+ 1,
+ plugin.checkSyntacticErrors,
+ plugin.vue
+ );
+
+ checker.nextIteration();
+ }
+
+ it('should create a Vue program config if vue=true', function () {
+ createCompiler({ vue: true });
+
+ var fileFound;
+
+ fileFound = checker.programConfig.fileNames.indexOf(files['example.vue']) >= 0;
+ expect(fileFound).to.be.true;
+
+ fileFound = checker.programConfig.fileNames.indexOf(files['syntacticError.ts']) >= 0;
+ expect(fileFound).to.be.true;
+ });
+
+ it('should not create a Vue program config if vue=false', function () {
+ createCompiler();
+
+ var fileFound;
+
+ fileFound = checker.programConfig.fileNames.indexOf(files['example.vue']) >= 0;
+ expect(fileFound).to.be.false;
+
+ fileFound = checker.programConfig.fileNames.indexOf(files['syntacticError.ts']) >= 0;
+ expect(fileFound).to.be.true;
+ });
+
+ it('should create a Vue program if vue=true', function () {
+ createCompiler({ vue: true });
+
+ var source;
+
+ source = checker.program.getSourceFile(files['example.vue']);
+ expect(source).to.not.be.undefined;
+
+ source = checker.program.getSourceFile(files['syntacticError.ts']);
+ expect(source).to.not.be.undefined;
+ });
+
+ it('should not create a Vue program if vue=false', function () {
+ createCompiler();
+
+ var source;
+
+ source = checker.program.getSourceFile(files['example.vue']);
+ expect(source).to.be.undefined;
+
+ source = checker.program.getSourceFile(files['syntacticError.ts']);
+ expect(source).to.not.be.undefined;
+ });
+
+ it('should get syntactic diagnostics from Vue program', function () {
+ createCompiler({ tslint: true, vue: true });
+
+ const diagnostics = checker.program.getSyntacticDiagnostics();
+ expect(diagnostics.length).to.be.equal(1);
+ });
+
+ it('should not find syntactic errors when checkSyntacticErrors is false', function (callback) {
+ createCompiler({ tslint: true, vue: true });
+
+ compiler.run(function(error, stats) {
+ expect(stats.compilation.errors.length).to.be.equal(2);
+ callback();
+ });
+ });
+
+ it('should find syntactic errors when checkSyntacticErrors is true', function (callback) {
+ createCompiler({ tslint: true, vue: true, checkSyntacticErrors: true });
+
+ compiler.run(function(error, stats) {
+ expect(stats.compilation.errors.length).to.be.equal(3);
+ callback();
+ });
+ });
+});
\ No newline at end of file
diff --git a/test/integration/vue/src/css.d.ts b/test/integration/vue/src/css.d.ts
new file mode 100644
index 00000000..ae764235
--- /dev/null
+++ b/test/integration/vue/src/css.d.ts
@@ -0,0 +1,5 @@
+declare module '*.css' {
+ const css = '';
+
+ export default css;
+}
diff --git a/test/integration/vue/src/example-wild.vue b/test/integration/vue/src/example-wild.vue
new file mode 100644
index 00000000..37bf9981
--- /dev/null
+++ b/test/integration/vue/src/example-wild.vue
@@ -0,0 +1,24 @@
+
+
+
Example
+
+
+
+
+
+
diff --git a/test/integration/vue/src/example.css b/test/integration/vue/src/example.css
new file mode 100644
index 00000000..da5d2635
--- /dev/null
+++ b/test/integration/vue/src/example.css
@@ -0,0 +1,3 @@
+.test {
+ background-color: blue;
+}
\ No newline at end of file
diff --git a/test/integration/vue/src/example.vue b/test/integration/vue/src/example.vue
new file mode 100644
index 00000000..4892c15d
--- /dev/null
+++ b/test/integration/vue/src/example.vue
@@ -0,0 +1,24 @@
+
+
+
Example
+
+
+
+
+
+
diff --git a/test/integration/vue/src/index.ts b/test/integration/vue/src/index.ts
new file mode 100644
index 00000000..ba753b87
--- /dev/null
+++ b/test/integration/vue/src/index.ts
@@ -0,0 +1,8 @@
+import ExampleWild from "@/example-wild.vue";
+import Example from "./example.vue";
+
+const foo = new Example();
+foo.msg = "foo";
+
+const bar = new ExampleWild();
+bar.msg = "bar";
diff --git a/test/integration/vue/src/syntacticError.ts b/test/integration/vue/src/syntacticError.ts
new file mode 100644
index 00000000..1655a39e
--- /dev/null
+++ b/test/integration/vue/src/syntacticError.ts
@@ -0,0 +1,2 @@
+// Syntactic error
+const array = [{} {}];
diff --git a/test/integration/vue/tsconfig.json b/test/integration/vue/tsconfig.json
new file mode 100644
index 00000000..6e26e3b4
--- /dev/null
+++ b/test/integration/vue/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "compilerOptions": {
+ "experimentalDecorators": true
+ },
+ "include": [
+ "src/**/*.ts",
+ "src/**/*.vue"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+}
diff --git a/test/integration/vue/tslint.json b/test/integration/vue/tslint.json
new file mode 100644
index 00000000..dfff889c
--- /dev/null
+++ b/test/integration/vue/tslint.json
@@ -0,0 +1,9 @@
+{
+ "defaultSeverity": "warning",
+ "extends": [
+ "tslint:recommended"
+ ],
+ "jsRules": {},
+ "rules": {},
+ "rulesDirectory": []
+}
\ No newline at end of file
diff --git a/test/unit/VueProgram.spec.js b/test/unit/VueProgram.spec.js
new file mode 100644
index 00000000..6a2944cd
--- /dev/null
+++ b/test/unit/VueProgram.spec.js
@@ -0,0 +1,66 @@
+var describe = require('mocha').describe;
+var it = require('mocha').it;
+var expect = require('chai').expect;
+var VueProgram = require('../../lib/VueProgram');
+
+describe('[UNIT] VueProgram', function () {
+ it('should determine if file is a Vue file', function() {
+ expect(VueProgram.isVue('./test.vue')).to.be.true;
+ expect(VueProgram.isVue('../test.vue')).to.be.true;
+ expect(VueProgram.isVue('../../test.vue')).to.be.true;
+ expect(VueProgram.isVue('@/test.vue')).to.be.true;
+ expect(VueProgram.isVue('../../.vue')).to.be.false;
+ expect(VueProgram.isVue('./test.css')).to.be.false;
+ expect(VueProgram.isVue('./')).to.be.false;
+ });
+
+ it('should properly resolve relative module names', function() {
+ var basedir = '/base/dir';
+ var containingFile = '/con/tain/ing/main.ts';
+ var options = {
+ baseUrl: '/baseurl',
+ paths: {
+ '@/*': [
+ 'src/*'
+ ]
+ }
+ }
+ var moduleNames = [
+ './test.vue',
+ '../test.vue',
+ '../../test.vue'
+ ];
+
+ var resolvedModuleNames = moduleNames.map(function(moduleName) {
+ return VueProgram.resolveNonTsModuleName(moduleName, containingFile, basedir, options);
+ });
+
+ expect(resolvedModuleNames[0]).to.be.equal('/con/tain/ing/test.vue');
+ expect(resolvedModuleNames[1]).to.be.equal('/con/tain/test.vue');
+ expect(resolvedModuleNames[2]).to.be.equal('/con/test.vue');
+ });
+
+ it('should properly resolve wildcard module names', function() {
+ var basedir = '/base/dir';
+ var containingFile = '/con/tain/ing/main.ts';
+ var options = {};
+ var moduleName = '@/test.vue';
+
+ resolvedModuleName = VueProgram.resolveNonTsModuleName(moduleName, containingFile, basedir, options);
+ expect(resolvedModuleName).to.be.equal('/base/dir/src/test.vue');
+
+ options.baseUrl = '/baseurl1';
+ resolvedModuleName = VueProgram.resolveNonTsModuleName(moduleName, containingFile, basedir, options);
+ expect(resolvedModuleName).to.be.equal('/baseurl1/src/test.vue');
+
+ options.baseUrl = '/baseurl2';
+ options.paths = { '@/*': ['src1/*'] }
+ resolvedModuleName = VueProgram.resolveNonTsModuleName(moduleName, containingFile, basedir, options);
+ expect(resolvedModuleName).to.be.equal('/baseurl2/src1/test.vue');
+
+ options.baseUrl = '/baseurl3';
+ options.paths = { '@/*': ['src1/src2/*'] }
+ resolvedModuleName = VueProgram.resolveNonTsModuleName(moduleName, containingFile, basedir, options);
+ expect(resolvedModuleName).to.be.equal('/baseurl3/src1/src2/test.vue');
+ });
+});
\ No newline at end of file