.NetCore i TypeScript

0

Próbuję już chyba trzeci dzień. Im dalej w las, tym więcej drzew. Przeczytałem stertę artów i nic.
Mam aplikację webową w .NetCore MVC.

Miałem kilka plików .ts w globalnym scopie. I wszystko ładnie działało. Dopóki nie zacząłem używać biblioteki AutoNumeric. Wtedy wszystko przestało działać i okazało się, że jeśli w jednym pliku robię import (import autonumeric), to już we wszystkim muszę robić import i export. I wszystko przestało działać. Ciągle dostaję jakieś błędy js w stylu 'export was not defined' albo, że funkcja, która jest w pliku js - nie istnieje. Czytam, czytam i nic. Poradzili mi, żebym zainstalował sobie webpacka. Już kilka godzin później nawet udało mi się coś uruchomić, ale rezultaty były dokładnie te same. Więc poszedłem w gulpa i zrobiłem dokładnie to, co było napisane w dokumentacji TypeScript. Efekt? Dokładnie taki sam.

Więc pytanie moje - jak KROK PO KROKU ożenić TypeScript z .NetCore MVC w taki sposób, żeby można było w TypeScript używać zewnętrznych modułów? Stawiam kratę browara

0

A jak wygląda konfiguracja?

Poza tym NTF, .Net nie ma tu nic do rzeczy.

0

Aktualnie wygląda to tak (poczyszczone z webpacka i gulpa):

tsconfig.json:

{
  "compilerOptions": {
    "noImplicitAny": false,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": true,
    "target": "ES5",
    "outDir": "wwwroot/js",
    "esModuleInterop": true
  },
  "compileOnSave": true,
  "include": [
    "scripts/**/*"
  ],
  "exclude": [
    "node_modules"
  ]
}

Drzewko projektu:

- wwwroot
    + css
    + imgs
    + js

+ node_modules
- Scripts
    - tutaj pliki *.ts

package.json:

{
  "version": "1.0.0",
  "name": "asp.net",
  "private": true,
  "devDependencies": {
    "@types/jquery": "3.5.0",
    "@types/bootstrap": "4.5.0",
    "@types/jquery-validation-unobtrusive": "3.2.32",
    "@syncfusion/ej2": "*",
    "autonumeric": "4.6.0"
  }
}
0

Taką mam konfigurację na podorędziu.

module.exports = {
  entry: './src/index.ts',
  devtool: 'inline-source-map',
  module: {
    rules: [
      {
        enforce: 'pre',
        use: 'tslint-loader',
        exclude: /node_modules/
      },
      {
        use: 'ts-loader',
        exclude: /node_modules/
      }
    ]
  },
  resolve: {
    extensions: ['.ts', '.js']
  },
  output: {
    filename: 'out.js',
    path: __dirname,
    library: 'asd',
    libraryTarget: 'var',
    libraryExport: 'default'
  }
}

Pozmieniałem tylko nazwy. Z taką konfiguracją powstanie jeden plik js, który będzie wystawiał moduł index.ts pod zmienną globalną asd.

0

Czy ja muszę mieć do tego coś konkretnego zainstalowane poza WebPackiem?
@tsz mam taki błąd:
ERROR in Entry module not found: Error: Can't resolve 'tslint-loader' in ...

0

Niestety, nic tu nie działa. Oto log z webpacka:

> webpack
Hash: 70d00b340606a72fdc94
Version: webpack 4.44.1
Time: 514ms
Built at: 2020-08-03 12:48:04
 1 asset
Entrypoint main = wwwroot/js/bundle.js
[0] ./Scripts/docview.ts 4.01 KiB {0} [not cacheable] [built] [failed] [1 error]
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
ERROR in ./Scripts/docview.ts
Module build failed (from ./node_modules/tslint-loader/index.js):
Error: Cannot find module 'tslint'
Require stack:
- D:\...\node_modules\tslint-loader\index.js
- D:\...\node_modules\loader-runner\lib\loadLoader.js
- D:\...\node_modules\loader-runner\lib\LoaderRunner.js
- D:\...\node_modules\webpack\lib\NormalModule.js
- D:\...\node_modules\webpack\lib\NormalModuleFactory.js
- D:\...\node_modules\webpack\lib\Compiler.js
- D:\...\node_modules\webpack\lib\webpack.js
- D:\...\node_modules\webpack-cli\bin\utils\validate-options.js
- D:\...\node_modules\webpack-cli\bin\utils\convert-argv.js
- D:\...\node_modules\webpack-cli\bin\cli.js
- D:\...\node_modules\webpack\bin\webpack.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:797:15)
    at Function.Module._load (internal/modules/cjs/loader.js:690:27)
    at Module.require (internal/modules/cjs/loader.js:852:19)
    at require (D:\work\projects\Testy\XMoney\node_modules\v8-compile-cache\v8-compile-cache.js:161:20)
    at Object.<anonymous> (D:\work\projects\Testy\XMoney\source\clients\WebClient\node_modules\tslint-loader\index.js:8:12)
    at Module._compile (D:\work\projects\Testy\XMoney\node_modules\v8-compile-cache\v8-compile-cache.js:194:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:995:10)
    at Module.load (internal/modules/cjs/loader.js:815:32)
    at Function.Module._load (internal/modules/cjs/loader.js:727:14)
    at Module.require (internal/modules/cjs/loader.js:852:19)
    at require (D:\work\projects\Testy\XMoney\node_modules\v8-compile-cache\v8-compile-cache.js:161:20)
    at loadLoader (D:\work\projects\Testy\XMoney\node_modules\loader-runner\lib\loadLoader.js:18:17)
    at iteratePitchingLoaders (D:\work\projects\Testy\XMoney\node_modules\loader-runner\lib\LoaderRunner.js:169:2)
    at iteratePitchingLoaders (D:\work\projects\Testy\XMoney\node_modules\loader-runner\lib\LoaderRunner.js:165:10)
    at D:\work\projects\Testy\XMoney\node_modules\loader-runner\lib\LoaderRunner.js:176:18
    at loadLoader (D:\work\projects\Testy\XMoney\node_modules\loader-runner\lib\loadLoader.js:47:3)
    at iteratePitchingLoaders (D:\work\projects\Testy\XMoney\node_modules\loader-runner\lib\LoaderRunner.js:169:2)
    at runLoaders (D:\work\projects\Testy\XMoney\node_modules\loader-runner\lib\LoaderRunner.js:365:2)
    at NormalModule.doBuild (D:\work\projects\Testy\XMoney\node_modules\webpack\lib\NormalModule.js:295:3)
    at NormalModule.build (D:\work\projects\Testy\XMoney\node_modules\webpack\lib\NormalModule.js:446:15)
    at Compilation.buildModule (D:\work\projects\Testy\XMoney\node_modules\webpack\lib\Compilation.js:739:10)
    at D:\work\projects\Testy\XMoney\node_modules\webpack\lib\Compilation.js:1111:12
    at D:\work\projects\Testy\XMoney\node_modules\webpack\lib\NormalModuleFactory.js:409:6
    at D:\work\projects\Testy\XMoney\node_modules\webpack\lib\NormalModuleFactory.js:155:13
    at AsyncSeriesWaterfallHook.eval [as callAsync] (eval at create (D:\work\projects\Testy\XMoney\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:6:1)
    at AsyncSeriesWaterfallHook.lazyCompileHook (D:\work\projects\Testy\XMoney\node_modules\tapable\lib\Hook.js:154:20)
    at D:\work\projects\Testy\XMoney\node_modules\webpack\lib\NormalModuleFactory.js:138:29
    at D:\work\projects\Testy\XMoney\node_modules\webpack\lib\NormalModuleFactory.js:346:9
    at processTicksAndRejections (internal/process/task_queues.js:75:11)
npm ERR! code ELIFECYCLE
npm ERR! errno 2
npm ERR! [email protected] build: `webpack`
npm ERR! Exit status 2
npm ERR! 
npm ERR! Failed at the [email protected] build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\ajachocki\AppData\Roaming\npm-cache\_logs\2020-08-03T10_48_04_707Z-debug.log

Process terminated with code 2.

Mój plik docview,ts buduje się bez problemów to JS. Nie ma żadnych błędów. Być może chodzi o jakieś importy. Początek wygląda tak:

import { AjaxManager } from "./ajaxmanager"
import { App } from "./app"
import { ModalDialog } from "./ModalDialog"
import { DocumentManager } from "./documentmanager"

Generalnie kompilator podczas budowania nie zgłasza żadnych błędów.

0

OK, ogarnąłem. Nie trzeba ani webpacka, ani gulpa, ani niczego takiego. Oto lista rzeczy, które trzeba zrobić, żeby mieć typescript na .NetCore Razor. Ten typscript jest debugowalny, ale pod Chromem. To już jest ograniczenie z MS. No to lecimy:

  1. Upewnij się, że masz zainstalowane node.js w Visual Studio. Po prostu uruchom Visual Studio Installer, kliknij guzik "Modify" przy Twojej wersji Visuala, znajdź moduł Node.js i upewnij się, że jest zainstalowany.
  2. Wykonaj kroki wymagane do dodania TypeScript do Twojego projektu. Wszystko jest opisane tutaj: https://docs.microsoft.com/pl-pl/visualstudio/javascript/tutorial-aspnet-with-typescript?view=vs-2019
  3. Upewnij się, że plik package.json ma takie wpisy w devDependencies:
"@types/jquery": "3.5.0",
"@types/bootstrap": "4.5.0",
"@types/jquery-validation-unobtrusive": "3.2.32"

i inne, których używasz. To powyżej po prostu dodaje do TypeScript typy dla JQuery, Bootstrap i JQueryValidation. JQuery, Bootstrap i JQueryValidation są domyślnie używane przez .NetCore Razor Pages/MVC, więc podejrzewam, że też tego używacie.

  1. Upewnij się, że Twój tsconfig.json wygląda tak:
{
  "compilerOptions": {
    "noImplicitAny": false,
    "noEmitOnError": true,
    "removeComments": false,
    "sourceMap": true,
    "target": "ES6",
    "outDir": "wwwroot/js",
    "esModuleInterop": true,
    "module": "AMD",
    "moduleResolution": "Node"
  },
  "compileOnSave": true,
  "include": [
    "scripts/**/*"
  ],
  "exclude": [
    "node_modules"
  ]
}

Najważniejsze rzeczy tutaj to:

  • target ustawiony na ES6 (możesz próbować też z ES5)
  • esModuleInterop ustawiony na true
  • module ustawione na AMD (to oznacza, że moduły będą ładowane asynchronicznie - to chyba domyślne ustawienie dla przeglądarek)
  • moduleResolution ustawione na Node
  1. W swoim pliku .ts używaj exportów i importów. Na przykład, plik person.ts:
export class Person
{
   //kod
}

export function foo()
{
  //kod
}

W drugim pliku, np:

import { Person } from "./person";

export class ClassThatUsesPerson
{
    _person: Person;
   //kod
}

Upewnij się, że importujesz pliki BEZ rozszerzenia. Czyli "./person" zamiast "./person.ts". Też "./" powinno znaleźć się na początku.

  1. Możesz importować inne biblioteki, których używasz i zainstalowałeś za pomocą npm - w podobny sposób. Np. ja zainstalowałem i używam autonumeric, więc importuję to tak:

import AutoNumeric from 'autonumeric'

  1. Pobierz bibliotekę requireJS (google) - to jest kluczowe
  2. Teraz kilka zmian w pliku _Layout.cshtml
  • pozbądź się jQuery i bootstrap z nagłówków. Zakładając, że skopiowałeś requireJS do "~/libs/requirejs.js", powinieneś dodać takie skrypty:
    <script src="~/lib/requirejs.js"></script>
    <script src="~/js/entrypoint.js"></script>

Zakładam, że trzymasz swoje pliki js w katalogu "~/js". A więc teraz powinieneś stworzyć tam plik entrypoint.js, który powinien wyglądać mniej więcej tak:

requirejs.config({
    baseUrl: "/js",
    shim: {
        bootstrap: {
            "deps": ["jquery"]
        }
    },
    paths: {
        jquery: "https://ajax.googleapis.com/ajax/libs/jquery/3.5.0/jquery.min",
        bootstrap: "https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.bundle.min",
        "jquery-validation": "https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.validate.min",
        "jquery.validate.unobtrusive": "https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.11/jquery.validate.unobtrusive.min"
    }
});

require(["jquery", "bootstrap", "app"], function (jq, bs, app) {
    app.App.init();
});

Teraz kilka słów wyjaśnienia.
Na początku konfigurujesz bibliotekę requireJS (poczytaj o konfiguracji na stronie tej biblioteki). W dużym skrócie: baseUrl to domyślne miejsce, w którym requireJS będzie szukał Twoich plików js.
Shim jest potrzebny do bootstrapa. Paths - tutaj definiujesz ścieżki do modułów. Po prostu dodaj URLe do modułów, które ładujesz z netu. Tutaj jQuery, bootstrap i jqueryValidations są potrzebne, żeby móc używać jQuery, bootstrap i walidacji :)
Ważne - upewnij się, że includujesz bootstrap.bundle.min, a nie bootstrap.min. Wersja bundle ma kilka innych bibliotek już w sobie (np. popper.js), co baaardzo ułatwia inne rzeczy. No i zmniejsza ilość requestów jakie wychodzą.

UWAGA! Nazwy modułów "jquery-validation" i "jquery.validate.unobtrusive" są istotne. Inaczej walidacja nie będzie działać. Te moduły (głównie jquery-validation) muszą nazywać się dokładnie tak.

Po konfiguracji widzisz taki kod:

require(["jquery", "bootstrap", "app"], function (jq, bs, app) {
    app.App.init();
});

To mówi po prostu: "Teraz załaduj moduły: jquery, bootstrap i app". Zauważ, że moduł app to jest mój moduł. init() to statyczna funkcja z klasy App. Robię tutaj rzeczy, które normalnie byłyby robione na document.ready lub body.onload.

W ostatnim kroku, upewnij się, że NIE MASZ nigdzie skryptów ładowanych w ten sposób: <script src="..."> (pamiętaj też o plikach utworzonych przez Visual Studio :)) Teraz skrypty ładujesz jedynie przez requirejs, używając funkcji require.

Więc na przykład zamiast:
<script src="~/js/person.js"></script>
powinieneś mieć:
require(["person"]);

Też nie musisz martwić się o zależności. Tzn. jeśli np. plik person.js ma zależności do innych plików (a w .ts zrobiłeś odpowiednie importy), to requirejs o wszystko zadba. A więc, jesli normalnie musiałeś zaincludować kilka skryptów:

<script src="~/js/lib1.js"><script>
<script src="~/js/person.js"></script>

(gdzie person.js zależał w jakiś sposób od lib1.js), to teraz includujesz tylko person:
require(["person"]);

A jeśli w pliku person masz jakąś klasę, możesz teraz utworzyć jej obiekt:

<script>
var personObj;
require(["person"], function(personFile) {
  personObj = new personFile.Person();
});
</script>

To tyle. Proste, nie? Hurray me! :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1