1.0.7 • Published 7 months ago

@corprio/gulp-typescript v1.0.7

Weekly downloads
-
License
ISC
Repository
github
Last release
7 months ago

About the package @corprio/gulp-typescript

https://www.npmjs.com/package/@corprio/gulp-typescript

This package is created for projects built in the ASP.NET Core MVC framework and desires to use typescript for each of its views. It intends to solve the problems: 1. Not getting intellisense or checkings for scripting in .cshtml views 2. If using separate script files, difficult to pass data from view to model 3. If using ES modules, the code inside becomes scoped and cannot access it from outside elsewhere in the view 4. If using ES modules, it may be bulky to serve all imported modules with the script

What it can do

Transpiles typescript

It will transpile each of your typescript files found in the Views folder into javascript files and outputs that into the wwwroot/js/views folder, under the same folder structure.

Project folder
+-- wwwroot
|  +-- js
|     +-- views
|        +-- Foo
|           +-- Bar.js //this javascript will be outputted
+-- Controllers
+-- Models
+-- Views
|  +-- Foo
|     +-- Bar.cshtml
|     +-- Bar.ts //this typescript file will transpile

It will also watch for changes in the typescript files to re-transpile.

Declares typing for javascript data model object in defined in view

Suppose you have a typescript file Bar.ts that is used in your view Bar.cshtml. How can you access the view model? You can no longer use @Model.MyProperty because you are not in the view file but in the script file.

What you might do is to define an object in your view like this.

<script type="text/javascript">
    var vdata = {
        myProperty = '@Model.MyProperty'
    };
</script>

Then in your script, you reference it.

doSomething(vdata.myProperty);

However, if you're using typescript, you'll an error Cannot find name: 'vdata', because vdata is not defined in the script. So you'll have to declare it first by adding

declare const vdata: any;

but when doing so, you will get no hint as to the shape of this vdata object.

So this package offers the solution by searching through the view and finding a definition of the vdata object, and then declares its shape into your typescript file. In the case above, it will automatically declare it at the top of your script, adding the following lines:

declare const vdata: {
    myProperty: string
}

It will also watch for changes in the view to re-declare.

Expose the javascript to global access

Suppose you use the import statement in your script to make use of third-party libraries, or to reuse code from your own modules. Your script is now a module because it contains either the import or the export statement.

So to include the script in the view you must add type="module"

<script type="module" src="~/js/views/Foo/Bar.js"></script>

And that is fine if all your scripting is done in that Bar.js, which is generally a good idea. But suppose you must access a function from Bar.js in your view.

In the case of many Corprio apps, we make use of the DevExtreme library for ASP.NET Core MVC, which allows us to write in C# that will in turn transpile into javascript for us later at run-time. Let's say you have something like this in your view:

@Html.DevExtreme().TextBoxFor(m => m.MyProperty).OnInitialized("initTextBox")

Here, you want to access initTextBox function from the view, which you have previously defined in your script. But your script is a module and so it is scoped and not accessible globally.

import * as anotherModule from './anotherModule.js' //this will hence make it a module

//cannot access this function globally
function initTextBox(e){
    ...
}

How this package solves the problem is it will wrap the script inside a UMD module under a namespace, say vjs. That way you can expose the initTextBox function by exporting it.

export function initTextBox(e){
    ...
}

Then in your view, access it under the vjs namespace.

@Html.DevExtreme().TextBoxFor(m => m.MyProperty).OnInitialized("vjs.initTextBox")

Treeshaking

Suppose you import something in your script.

import * as anotherModule from './anotherModule.js' 

function initTextBox(e){
    anotherModule.addEffect(e);
}

You script now imports anotherModule and it will be included in the output. But anotherModule might be a large module with many functions, and you are only using one of it.

This packages solves this by treeshaking it with webpack, so that only what is used will be included. And whatever is not used will not be bundled together.

How to use

1. Install the package

Run this command in the root of your project where the package.json is.

npm install @corprio/gulp-typescript --save-dev

2. Create a tsconfig.json

Add a configuration file for typescript in the root of your project, with the following content.

{
  "compilerOptions": {
    "forceConsistentCasingInFileNames": true,
    "noImplicitAny": false,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": true,
    "target": "es6",
    "module": "esnext",
    "moduleResolution": "nodenext",
    //if using corprio.js
    "types": [ "jquery", "file-saver", "bootstrap", "node" ]
  },
  "include": [
    "Views/**/*",
    //if using corprio.js
    "node_modules/@corprio/aspnetcore-site/dist/js/**/*"
  ]
}

This file is important because this package's gulp tasks will transpile the typescript based on settings in the tsconfig.json.

A brief explanation of what those configs do:

The include configures which files the compiler should include. Here, Views/**/* told it to include all the typescript files from the Views folder, because those are the files we need to transpile to javascript. If you're using corprio.js from the @corprio/aspnetcore-site package, You should also include node_modules/@corprio/aspnetcore-site/dist/js/**/* so that the declaration files for corprio.js will be included. This will also include .d.ts files for DevExtreme because corprio.js uses it.

The types configures which typings it should include from node_modules/@types. For example, if you're using jquery then you should include that here, given you have already installed the @types/jquery package. Here, we are including jquery, file-saver, bootstrap, node because corprio.js uses them.

The module is set to esnext so that dynamic imports are supported. The target is set to es6, which the gulp tasks from this package will use to further treeshake and bundle.

As for the other settings, you can see what they do in https://www.typescriptlang.org/tsconfig.

3. Set TypeScriptCompileBlocked to true

Add the following lines to your .csproj project file.

<PropertyGroup>
    <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
</PropertyGroup>

This will tell Visual Studio to not run typescript compile on MSBuild. We will take care of that with gulp instead.

4. Install gulp

You will need gulp to run the tasks that are included in this package. Go ahead and install gulp as well.

npm install gulp --save-dev

5. Create a gulpfile.js

Create a gulpfile.js in the root of your project. Add the following content inside.

const gulpTs = require('@corprio/gulp-typescript');

//deletes everything inside wwwroot/js/views folder
exports.cleanJsViews = gulpTs.cleanJsViews;

//process typescript files to transpile to js files, and add declarations to ts from view
//then keep watching for changes
exports.processTs = gulpTs.processTs;

//cleans www/js/views and repopulate with transpiled js, refresh declarations in ts,
//keeps watching for changes in views and ts to update
exports.refreshAll = gulpTs.refreshAll;

//watch for changes in typescript files and their views to transpile or generate declaration on demand
exports.watchTsAndViews = gulpTs.watchTsAndViews;

The gulpfile.js contains tasks that can be run to perform pipeline processes for your client-side resources. Here we have imported our package into the gulpTs variable. Then we retrieved four different tasks from the package and exported them. When we export a task, we make it available for us to run via the Task Runner Explorer.

6. Run the processTs task

Open Task Runner Explorer from Visual Studio found under View > Other Windows > Task Runner Explorer. You will now see the four exported tasks from our gulpfile.js. Right-click processTs and hit Run.

The processTs task will generate the javascript files for you under wwwroot/js/views. It will also add declarations of vdata into your typescript files. Then it will keep watching for changes and redo that accordingly.

7. Watch for change on project open

Now that you have everything generated, you won't need to keep right-clicking and running the task every time. You just have to tell gulp to keep watching for changes every time the project opens.

To do so, open the Task Runner Explorer panel, right-click on watchTsAndViews and click Bindings, select Project Open.

This will actually add the following line to the top of your gulpfile:

/// <binding ProjectOpened='watchTsAndViews' />

So now every time you open the project, this watchTsAndViews task will run, and keep watching and processing as you go.

Documentation

initConfig(config: InitOptions)

Initialize configurations to override the defaults. Just run the function from your gulpfile.js, before any other tasks are executed. If the defaults works fine for you, just don't run this function.

const gulpTs = require('@corprio/gulp-typescript');

gulpTs.initConfig({
    jsModuleNamespace: 'vjs',
    viewDataObjName: 'vdata',
    srcPath: 'Views/**/*.ts'
    destPath: 'wwwroot/js/views/'
});

InitOptions

jsModuleNamespace: string

Default value: 'vjs'

Namespace for the output js UMD modules. This exposes it globally so the view can access any exported functions from the script.

viewDataObjName: string

Default value: 'vdata'

Name of the js object which is defined in the view that you wish to make known to the script file. A typescript declaration will be generated in the script file for that object.

srcPath: string | string []

Default value: 'Views/*/.ts'

The glob pattern(s) that points to the typescript files that you wish to process.

destPath: string

Default value: 'wwwroot/js/views/'

The file path to the destination folder where you wish the javascript files to be outputted.

cleanJsViews()

A gulp task that deletes all files from destPath (default: 'wwwroot/js/views'). Use with caution as it erases everything from the folder. Make sure to run transpileAll() again after this action to regenerate the javascript files.

processTs_PRODUCTION()

A gulp task that processes typescript files to transpile to javascript files, and adds type declarations to typescript files of the viewDataObjName (default: 'vdata') object as defined in the view. Then keeps watching for changes to transpile or declare on demand.

Basically, it runs

gulp.parallel(
    gulp.series(declareAll, transpileAll),
    watchDeclare
)

It will transpile to both minified and non-minified scripts.

processTs_DEVELOPMENT()

Same as processTs_PRODUCTION() but It will only transpile to non-minified scripts.

refreshAll_PRODUCTION()

A gulp task that cleans the output folder and regenerates, then keeps watching.

Basically, it runs

gulp.series(
    cleanJsViews, processTs
)

It will transpile to both minified and non-minified scripts.

refreshAll_DEVELOPMENT()

Same as refreshAll_PRODUCTION() but It will only transpile to non-minified scripts.

watchTsAndViews_PRODUCTION()

A gulp task that watches each typescript file for change. On change, it will transpile the javascript for that file. It also watches each .cshtml file that has an associated typescript file (Bar.cshtml and Bar.ts are associated because they share the same file name). On change, it will declare the type declaration for viewDataObjName (default: 'vdata') in the typescript file.

It will transpile to both minified and non-minified scripts.

watchTsAndViews_DEVELOPMENT()

Same as watchTsAndViews_PRODUCTION() but It will only transpile to non-minified scripts.

transpileAllJs()

A gulp task that transpiles each typescript files that matches the pattern in srcPath (default: 'Views/*/.ts') into javascript UMD modules, under the namespace jsModuleNamespace: string (default: 'vjs'). It will keep the same folder structure and file name as the source.

For example, these typescript files:

+-- Views
|  +-- Fruits
|     +-- Apple.cshtml
|     +-- Apple.ts 
|     +-- Orange.cshtml
|     +-- Orange.ts 
|  +-- Veggies
|     +-- Lettuce.cshtml
|     +-- Lettuce.ts 

Will output the following folders and javascript files.

Project folder
+-- wwwroot
|  +-- js
|     +-- views
|        +-- Fruits
|           +-- Apple.js 
|           +-- Orange.js 
|        +-- Veggies
|           +-- Lettuce.js 

Any imports made in the typescript file, will be bundled into the javascript file, after treeshaking. Note: this is only possible if the imports are of ES modules.

transpileAllMinJs()

Same as transpileAllJs() but transpiles to minified scripts.

declareAll()

A gulp task that adds type declarations to typescript files of the viewDataObjName (default: 'vdata') object as defined in the associated view.

You may have this in your view Apple.cshtml

<script type="text/javascript">
    var vdata = {
        model: {
            color = '@Model.Color',
            fridgeLife = number('@Model.FridgeLife'),
            isSoldOut = '@Model.IsSoldOut' == 'true',
        },
        link = '@Url.Action("Apple", "Fruits")'
    };
</script>

This will add the following to the top of Apple.ts

declare const vdata: {
    model: {
        color: string;
        fridgeLife: number;
        isSoldOut: boolean;
    };
    link: string;
}

watchDeclare()

A gulp task that watches each .cshtml file that has an associated typescript file (Bar.cshtml and Bar.ts are associated because they share the same file name). On change, it will declare the type declaration for viewDataObjName (default: 'vdata') in the typescript file.