Cannot resolve interface defined in the same module but in different file - module

Below the code
fields/base.ts
/// <reference path="../_all.ts" />
module SomeApp.fields {
export interface IField {}
export class Field implements IFields {}
}
in fields/CharField.ts
/// <reference path="../_all.ts" />
module SomeApp.fields {
import IField = SomeApp.fields.IField;
export interface ICharField extends IField {}
export class Field implements IFields {}
}
in _all.ts:
/// <reference path="fields/base.ts" />
/// <reference path="fields/CharField.ts" />
but this does not work, and I got this error from CharField.ts file:
Error:(7, 39) TS2305: Module 'SomeApp.fields' has no exported member
'IField'.
So what's wrong in my code ?

Your problem raises a few questions:
How are you compiling this?
Wy is _all.ts a .ts file and not a .d.ts file?
Why are you importing IField (depends on point "1")?
Here is a working example:
.
+-- tsconfig.json
+-- references.d.ts
+-- src
| +-- myInterface.ts
| +-- myClass.ts
tsconfig.json:
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"out": "result.js",
"sourceMap": true
},
"files": [
"src/myClass.ts"
]
}
references.d.ts:
/// <reference path="src/myInterface.ts" />
/// <reference path="src/myClass.ts" />
myInterface.ts:
module MyNameSpace {
export interface IMyInterface {
// ...
}
}
myClass.ts:
/// <reference path="../references.d.ts" />
module MyNameSpace {
export class MyClass implements IMyInterface {
// ...
}
}
The example above works, exactly why your example does not work might be to any of the 1, 2, or 3 points mentioned above.

Related

How do I annotate an endpoint in NestJS for OpenAPI that takes Multipart Form Data

My NestJS server has an endpoint that accepts files and also additional form data
For example I pass a file and a user_id of the file creator in the form.
NestJS Swagger needs to be told explicitly that body contains the file and that the endpoint consumes multipart/form-data this is not documented in the NestJS docs https://docs.nestjs.com/openapi/types-and-parameters#types-and-parameters.
Luckily some bugs led to discussion about how to handle this use case
looking at these two discussions
https://github.com/nestjs/swagger/issues/167
https://github.com/nestjs/swagger/issues/417
I was able to put together the following
I have added annotation using a DTO:
the two critical parts are:
in the DTO add
#ApiProperty({
type: 'file',
properties: {
file: {
type: 'string',
format: 'binary',
},
},
})
public readonly file: any;
#IsString()
public readonly user_id: string;
in the controller add
#ApiConsumes('multipart/form-data')
this gets me a working endpoint
and this OpenAPI Json
{
"/users/files":{
"post":{
"operationId":"UsersController_addPrivateFile",
"summary":"...",
"parameters":[
],
"requestBody":{
"required":true,
"content":{
"multipart/form-data":{
"schema":{
"$ref":"#/components/schemas/UploadFileDto"
}
}
}
}
}
}
}
...
{
"UploadFileDto":{
"type":"object",
"properties":{
"file":{
"type":"file",
"properties":{
"file":{
"type":"string",
"format":"binary"
}
},
"description":"...",
"example":"'file': <any-kind-of-binary-file>"
},
"user_id":{
"type":"string",
"description":"...",
"example":"cus_IPqRS333voIGbS"
}
},
"required":[
"file",
"user_id"
]
}
}
Here is what I find a cleaner Approach:
#Injectable()
class FileToBodyInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const ctx = context.switchToHttp();
const req = ctx.getRequest();
if(req.body && req.file?.fieldname) {
const { fieldname } = req.file;
if(!req.body[fieldname]) {
req.body[fieldname] = req.file;
}
}
return next
.handle();
}
}
const ApiFile = (options?: ApiPropertyOptions): PropertyDecorator => (
target: Object, propertyKey: string | symbol
) => {
ApiProperty({
type: 'file',
properties: {
[propertyKey]: {
type: 'string',
format: 'binary',
},
},
})(target, propertyKey);
};
class UserImageDTO {
#ApiFile()
file: Express.Multer.File; // you can name it something else like image or photo
#ApiProperty()
user_id: string;
}
#Controller('users')
export class UsersController {
#ApiBody({ type: UserImageDTO })
// #ApiResponse( { type: ... } ) // some dto to annotate the response
#Post('files')
#ApiConsumes('multipart/form-data')
#UseInterceptors(
FileInterceptor('file'), //this should match the file property name
FileToBodyInterceptor, // this is to inject the file into the body object
)
async addFile(#Body() userImage: UserImageDTO): Promise<void> { // if you return something to the client put it here
console.log({modelImage}); // all the fields and the file
console.log(userImage.file); // the file is here
// ... your logic
}
}
FileToBodyInterceptor and ApiFile are general, I wish they where in the NestJs
You probably need to install #types/multer to have to Express.Multer.File

Import json data as sass variables in an .scss file Nuxt

I'm trying to import json varialbes in a scss file so i can have them defined in 1 place and later use them in both scss and js.
The application is vue/nuxt
Have tried many variants but without success
Here is my code
src/assets/scss/test.json
{
"danger": "#cc3333",
"info": "#3399ff",
"success": "#33cc99",
"warning": "#ffcc00"
}
src/assets/scss/main.scss
#import "./test.json";
body {
background-color: $info;
}
nuxt.config.js
const jsonImporter = require('node-sass-json-importer');
module.exports = {
css: [
'#/assets/scss/main.scss',
],
...
/*
** Build configuration
*/
build: {
/*
** You can extend webpack config here
*/
extend (config, ctx) {
ctx.loaders.sass.sassOptions.importer = jsonImporter
console.log(ctx.loaders.sass.sassOptions)
}
}
}
Gives me error
Module build failed (from ./node_modules/sass-loader/dist/cjs.js): friendly-errors 17:22:43
SassError: Invalid CSS after "{": expected 1 selector or at-rule, was "{"
on line 1 of assets/scss/test.json
from line 1 of C:\Users\Fluksikarton\Desktop\nuxt-webserotonin-template\assets\scss\main.scss
>> {
extend (config, ctx) {
console.log(ctx.loaders.scss)
ctx.loaders.scss.scssOptions = {};
ctx.loaders.scss.scssOptions.importer = jsonImporter()
}
Gives
ValidationError: Invalid options object. Sass Loader has been initialised using an options object that does not match the API schema.
- options has an unknown property 'importer'. These properties are valid:
object { implementation?, sassOptions?, prependData?, sourceMap?, webpackImporter? }
Default export from node-sass-json-importer is a factory function, not the importer itself.
Also you are using SCSS not SASS ....
Change it to this: ctx.loaders.scss.sassOptions.importer = jsonImporter()

Set programmatically jsonValidation for dynamic mapping

I am creating a new vscode extension, and I need to extend the standard usage of the jsonValidation system already present in vscode.
Note : I am talking about the system defined in package.json :
"contributes" : {
"languages": [
{
"id" : "yml",
"filenamePatterns": ["module.service"]
},
{
"id" : "json",
"filenamePatterns": ["module.*"]
}
],
"jsonValidation": [
{
"fileMatch": "module.test",
"url": "./resources/test.schema"
}
]
}
Now, I need to create a dynamic mapping, where the json fields filematch/url are defined from some internal rules (like version and other internal stuff). The standard usage is static : one fileMatch -> one schema.
I want for example to read the version from the json file to validate, and set the schema after that :
{
"version" : "1.1"
}
validation schema must be test-schema.1.1 instead of test-schema.1.0
note : The question is only about the modification of the configuration provided by package.json from the extensions.ts
Thanks for the support
** EDIT since the previous solution was not working in all cases
There is one solution to modify the package.json at the activating of the function.
export function activate(context: vscode.ExtensionContext) {
const myPlugin = vscode.extensions.getExtension("your.plugin.id");
if (!myPlugin)
{
throw new Error("Composer plugin is not found...")
}
// Get the current workspace path to found the schema later.
const folderPath = vscode.workspace.workspaceFolders;
if (!folderPath)
{
return;
}
const baseUri : vscode.Uri = folderPath[0].uri;
let packageJSON = myPlugin.packageJSON;
if (packageJSON && packageJSON.contributes && packageJSON.contributes.jsonValidation)
{
let jsonValidation = packageJSON.contributes.jsonValidation;
const schemaUri : vscode.Uri = vscode.Uri.joinPath(baseUri, "/schema/value-0.3.0.json-schema");
const schema = new JsonSchemaMatch("value.ospp", schemaUri)
jsonValidation.push(schema);
}
}
And the json schema class
class JsonSchemaMatch
{
fileMatch: string;
url : string;
constructor(fileMatch : string, url: vscode.Uri)
{
this.fileMatch = fileMatch;
this.url = url.path;
}
}
Another important information is the loading of the element of contributes is not reread after modification, for example
class Language
{
id: string;
filenamePatterns : string[];
constructor(id : string, filenamePatterns: string[])
{
this.id = id;
this.filenamePatterns = filenamePatterns;
}
}
if (packageJSON && packageJSON.contributes && packageJSON.contributes.languages)
{
let languages : Language[] = packageJSON.contributes.languages;
for (let language of languages) {
if (language.id == "json") {
language.filenamePatterns.push("test.my-json-type")
}
}
}
This change has no effect, since the loading of file association is already done (I have not dig for the reason, but I think this is the case)
In this case, creating a settings.json in the workspace directory can do the job:
settings.json
{
"files.associations": {
"target.snmp": "json",
"stack.cfg": "json"
}
}
Be aware that the settings.json can be created by the user with legitimate reason, so don't override it, just fill it.

How to import ion-rangeslider in Aurelia

I am trying to use the ionRangeSlider plugin in Aurelia but am not sure how to make use of it.
https://github.com/IonDen/ion.rangeSlider/issues
I have jspm'd it into my project but how do I import it as well as call the one function that runs the plugin?
You will find the exact package names for including ion-rangesider in your package.json:
jspm": {
"dependencies": {
...
"ion-rangeslider": "npm:ion-rangeslider#^2.1.3",
"jquery": "npm:jquery#^2.2.3",
...
}
}
Then you need to create your own custom element like:
import {inject, noView} from 'aurelia-framework';
//import your dependencies
import $ from 'jquery';
import ionRangeSlider from 'ion-rangeslider';
#noView()
#inject(Element)
export class Slider {
constructor(element){
this.element = element;
}
bind(){
$(this.element).ionRangeSlider({min: 100, max: 1000, from: 550});
}
}
And where you want to use your slider you have to write:
<require from='./slider'></require>
<require from="ion-rangeslider/css/ion.rangeSlider.skinHTML5.css"></require>
<require from="ion-rangeslider/css/ion.rangeSlider.css"></require>
<slider></slider>
Normally you would put the <require from="xxx.css"></require> tags inside slider.html to ensure style encapsulation. In my example i put them where i wanted to use the slider because so i donĀ“t needed to create a slider.html.
Here is an example how to use the bootstrap popover.
I guess you should be able to do the same and calling $("#example_id").ionRangeSlider(); from within the bind function
if you imported all resources
Install ion-rangeslider first:
npm install ion-rangeslider
jspm install npm:ion-rangeslider
Create a custom attribute
import {customAttribute, bindable, inject} from 'aurelia-framework';
import {ionRangeSlider} from 'ion-rangeslider';
#customAttribute('rangeslider')
#inject(Element)
export class RangesliderCustomAttribute {
//make your own options based on requirements
options = { type: "single", min: 0, max: 100 };
constructor(element) {
this.element = element;
}
attached() {
$(this.element).ionRangeSlider(this.options).on('change', e => {
fireEvent(e.target, 'input');
});
}
detached() {
$(this.element).ionRangeSlider('destroy').off('change');
}
}
function createEvent(name) {
var event = document.createEvent('Event');
event.initEvent(name, true, true);
return event;
}
function fireEvent(element, name) {
var event = createEvent(name);
element.dispatchEvent(event);
}
import css into app.html or where you import css in your application
<require from="ion-rangeslider/css/ion.rangeSlider.css"></require>
<require from="ion-rangeslider/css/ion.rangeSlider.skinNice.css"></require>
Now you can use your attribute in input in any view
<require from="./rangeslider"></require>
<input rangeslider type="text" value.bind="yourInitialSliderValue">

How to remap a Durandal viewmodel

This is my main.js file in a Durandal project.
What I'm trying to do is set things up so that the name 'upload-item' resolves to either 'upload-item' or 'upload-item-prehtml5' depending on whether File is defined.
requirejs.config({
paths: {
'text': '../lib/require/text',
'durandal': '../lib/durandal/js',
'plugins': '../lib/durandal/js/plugins',
'transitions': '../lib/durandal/js/transitions',
'knockout': '../lib/knockout/knockout-2.3.0',
'bootstrap': '../lib/bootstrap/js/bootstrap',
'jquery': '../lib/jquery/jquery-1.9.1.min',
'jquery-ui': '../lib/jquery-ui/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min',
'moment': '../lib/moment/moment',
'knockout-jqueryui': '../lib/knockout/knockout-jqueryui.min',
'file-size-formatting': '../lib/wone/file-size-formatting'
},
shim: {
'bootstrap': {
deps: ['jquery'],
exports: 'jQuery'
}
}
});
define(['durandal/system', 'durandal/app', 'durandal/viewLocator'], function (system, app, viewLocator) {
//>>excludeStart("build", true);
system.debug(true);
//>>excludeEnd("build");
var filetype = typeof(File);
if (filetype == 'undefined') {
//apply pre-html5 fixups
require.config({
map: {
'*': {
'upload-item': 'upload-item-prehtml5'
}
}
});
}
app.title = 'Jumbo File Transfer';
//specify which plugins to install and their configuration
app.configurePlugins({
router: true,
dialog: true,
widget: {
kinds: ['expander']
}
});
app.start().then(function () {
//Replace 'viewmodels' in the moduleId with 'views' to locate the view.
//Look for partial views in a 'views' folder in the root.
viewLocator.useConvention();
//Show the app by setting the root view model for our application.
app.setRoot('shell');
});
});
Testing on IE8 shows that the call to require.config occurs and the mapping is added, but it doesn't seem to have the effect I expected: upload-item.js and upload-item.html are loaded when I expected upload-item-prehtml5.js and upload-item-prehtml5.html to be loaded.
If this is the wrong way to go about this, then what is the right way to perform this kind of conditional resolution?
It's not quite what I originally wanted, but I found you can do this:
var prehtml5 = (typeof (File) == 'undefined');
requirejs.config({
paths: {
...
'upload-item': prehtml5 ? 'upload-item-prehtml5' : 'upload-item'
},
shim: {
'bootstrap': {
deps: ['jquery'],
exports: 'jQuery'
}
}
});
Path remapping seems to extend into the file name. Normally you wouldn't list siblings of main.js but you can and if you do then you can remap them, including the file name.