Javascript-форум (https://javascript.ru/forum/)
-   Оффтопик (https://javascript.ru/forum/offtopic/)
-   -   Разметка для Google closure compiler (https://javascript.ru/forum/offtopic/41168-razmetka-dlya-google-closure-compiler.html)

monolithed 02.09.2013 23:10

Разметка для Google closure compiler
 
/** @namespace foo */
def ('foo') ({
	/**
	 * @constructor
	 */
	__init__: function() {

              // WARNING - Property method never defined
              this.method();
	},
    /** @this {foo} */
	method: function() {
               // WARNING - Bad type annotation. Unknown type namespace
       }
});

 // WARNING - foo never defined
new foo;



Кто-нибудь знает как размечать такие конструкторы для GCC?

Gozar 03.09.2013 00:32

Он их ломает?

упс. Вижу, ломает.

monolithed 03.09.2013 01:25

Да много там сюрпризов:

/**
 * @param {Object} object
 * @param {(string|number)} name
 * @param {*} value
 */
var fabric = function(object, name, value) {
    object[name] = value;
};


fabric(Number, 'MAX_INTEGER', 9007199254740991);
// ...

console.log(Number.MAX_INTEGER); // 9007199254740991

// WARNING: JSC_INEXISTENT_PROPERTY: Property MAX_INTEGER never defined on Number at line 14 character 12


Без предварительного экспорта переменных через через опцию --externs=... сыпит ворнингами

devote 03.09.2013 10:35

Цитата:

Сообщение от monolithed
Разметка для Google closure compiler

боролся я как то с этим, хотя мне больше волновал вопрос с автокомплитом. В итоге вышло примерно так, GCC при этом не плачет и автокомплит работает хорошо:
/**
 * Component является общим предком всех классов компонентов.
 *
 * @author <a href="mailto:spb.piksel@gmail.com">Dmitrii Pakhtinov</a>
 * @requires Class
 * @version 1.0 - 12/20/2012
 */
Class("SP.Component", function() {
  /**
   * Ссылка на родительский компонент
   *
   * @type {SP.Component|null}
   * @private
   */
  var owner = null;
  /**
   * Список дочерних компонентов
   *
   * @type {Array}
   * @private
   */
  var components = [];
  /**
   * @class SP.Component
   */
  return {
    /**
     * Общий предок всех компонентов, каждый компонент обязан наследоватся от этого класса
     *
     * @namespace SP
     * @constructs
     * @constructor
     */
    Component: function() {
        // dummy
    },
    /**
     * Устанавливает или возвращает родительский компонент
     * Для удаления родительского компонента установите значение <b>null</b>
     *
     * Родительский компонент является владельцем компонента и может оперировать
     * свойствами дочернего компонента. Присвоеный компонент является владельцем
     * компонента которому его присвоили и в случае удаления владельца, будут
     * удалены все его подчиненные компоненты, если вы не хотите потерять подчиненные
     * копоненты, позаботьтесь об этом заранее
     *
     * @type {SP.Component|null}
     * @property
     * @public
     * @final
     */
    owner: {
      /**
       * @param {SP.Component|null} component
       */
      set: function(component) {

        // пропускаем только если текущая ссылка на владельца иная и это наследник компонента
        if (component !== owner && (component === null || Class.instanceOf(component, this.__class__))) {

          // удаляем у текущего владельца данный компонент
          if (owner) {
            // если владелец не позволит удалить компонет
            if (owner.onremovecomponent(this) === false) {
              return;
            }
            owner.removeComponent.call(false, this, owner);
          }

          // добавляем владельцу текущий компонент
          if (component) {
            // если компонент не позволит его добавить
            if (component.oninsertcomponent(this) === false) {
              return;
            }
            component.insertComponent.call(false, this, component);
          }

          // меняем ссылку на владельца
          owner = component;
        }
      },

      get: function() {
        return owner;
      }
    },

    /**
     * Добавляет в список дочерний компонент
     *
     * @param {SP.Component} component компонент который будет дочерним
     * @return {Boolean} возвращает <b>true</b> при успешном выполнении, в противном случае <b>false</b>
     * @public
     * @final
     * @see {@link get.components}
     */
    insertComponent: function(component) {

      // Условие для внутреннего использования
      if (this instanceof Boolean && arguments.length > 1) {

        // Добавляем компонент в список
        components[components.length] = component;

        // информируем компонент о том что его добавли владельцу
        component.oninsert(arguments[1]);

      } else if (Class.instanceOf(component, this.__class__)) {
        // Если объект является компонентом пробуем добавить его
        return components.length < (component.owner = this, components.length);
      }

      return false;
    },

    /**
     * Удаляет из списка дочерний компонент
     *
     * @param {SP.Component} component компонент который является дочерним этого компонента
     * @return {Boolean} возвращает <b>true</b> при успешном выполнении, в противном случае <b>false</b>
     * @public
     * @final
     * @see {@link get.components}
     */
    removeComponent: function(component) {

      // Условие для внутреннего использования
      if (this instanceof Boolean && arguments.length > 1) {

        var index = -1;

        if ("indexOf" in components) {
          // новая версия ECMAScript имеет поддержку метода indexOf для массивов
          index = components.indexOf(component);
        } else {
          // для более старой версии ECMAScript
          for(var i = components.length; i--;) {
            if (components[i] === component) {
              index = i;
              break;
            }
          }
        }

        if (index >= 0) {

          // удаляем компонент из стека
          components.splice(index, 1);

          // информируем компонент о том что его удалили из родительского компонента
          component.onremove(arguments[1]);
        }

      } else if (Class.instanceOf(component, this.__class__)) {
        // Если объект является компонентом удаляем у него родителя
        return components.length > (component.owner = null, components.length);
      }

      return false;
    },

    /**
     * Содержит список всех дочерних компонентов
     *
     * @type {Array}
     * @property
     * @public
     * @see {@link insertComponent}
     * @see {@link removeComponent}
     */
    components: {
      get:function() {
        // не будем возвращать ссылку на оригинальный список компонентов, отдаем копию
        return components.slice(0);
      }
    },

    /**
     * Устанавливает или возвращает позицию компонента
     *
     * Если параметр component не задан, то в случае если задан position установит
     * текущий компонент у его владельца на новую позицию, если же параметр position
     * так же не задан, то вернет текущую позицию у владельца.
     *
     * Если компонент не имеет владельца, вернет значение -1
     *
     * @param {SP.Component} [component]
     * @param {Integer} [position]
     * @return {Integer}
     * @public
     */
    componentPosition: function(component, position) {

      // ищем компонент у владельца
      if (typeof component === "number" || !component) {
        return owner ? owner.componentPosition(this, component) : -1;
      }

      var index = -1, length = components.length;

      if ("indexOf" in components) {
        // новая версия ECMAScript имеет поддержку метода indexOf для массивов
        index = components.indexOf(component);
      } else {
        // для более старой версии ECMAScript
        for(var i = components.length; i--;) {
          if (components[i] === component) {
            index = i;
            break;
          }
        }
      }

      // если позиция или компонент не определены, возвращаем текущуюю позицию
      if (typeof position !== "number" || index < 0) {
        return index;
      }

      // попытаемся нормализовать позицию
      while(position < 0 || position >= length) {
        position = position < 0 ? length + position : position >= length ? position - length : position;
      }

      // если текущая позиция не равна новой позиции
      if (index !== position) {

        // удаляем компонент из текущей позиции
        components.splice(index, 1);

        if (position === 0) {
          // вставляем в начало
          components.unshift(component);
        } else if (position === components.length) {
          // вставляем в конец
          components.push(component);
        } else {
          // вставляем где то в середине
          components = components.slice(0, position).concat(component, components.slice(position));
        }

        length = index < position ? position : index;
        index = index < position ? index : position;

        for( ; index <= length; index++ ) {
          components[index].onchangeposition(index);
        }
      }

      // возвращаем позицию компонента
      return position;
    },

    /**
     * Событие страбатывает у владельца в случае если добавляют в него новый компонент
     * Верните <b>false</b> если не хотите что бы в ваш компонент что-то добавляли.
     *
     * @protected
     * @param {SP.Component} component
     */
    oninsertcomponent: function(component) {
      /* virtual protected */
    },

    /**
     * Событие срабатывает у владельца при удалении из него компонента
     * Верните <b>false</b> если не хотите что бы из владельца что-то удаляли
     *
     * @protected
     * @param {SP.Component} component
     */
    onremovecomponent: function(component) {
      /* virtual protected */
    },

    /**
     * Событие срабатывает у компонента в том случае если он был успешно принят владельцем
     *
     * @protected
     * @param {SP.Component} owner
     */
    oninsert: function(owner) {
      /* virtual protected */
    },

    /**
     * Событие срабатывает если компонент успешно был удален владельцем
     *
     * @protected
     * @param {SP.Component} owner
     */
    onremove: function(owner) {
      /* virtual protected */
    },

    /**
     * Событие срабатывает если сменили позицию компонента
     *
     * @protected
     * @param {Integer} position
     */
    onchangeposition: function(position) {
      /* virtual protected */
    }
  }
});

monolithed 03.09.2013 10:47

Может у тебя такие ошибки игнорировались?

Потому что сразу же будет предупреждение:

JSC_TYPE_PARSE_ERROR: Bad type annotation. Unknown type SP.Component at line 12 character 9
* @type {SP.Component|null}


Больше всего меня конечно бесит это:

var global = function() {
    return this;
}();

void function(global) {

    global.foo = 1;

}(global);


Приходится выносить определение глобальной перменной в экстерны.

PS: мой конфиг:

#! /usr/bin/env sh

# Google closure compiler
# [url]http://code.google.com/p/closure-compiler/wiki/Warnings[/url]

timestamp=`date +%s`

compiler=../tools/closure-compiler/build/compiler.jar
output=../cache/static/__init__

java -jar ${compiler} \
	--js=${output}.js \
	--externs=../model/__slot__.js \
	--js_output_file="${output}.${timestamp}.js" \
	--create_source_map="${output}.${timestamp}.json" \
	--summary_detail_level=3 \
	--charset=UTF-8 \
	--language_in=ECMASCRIPT5_STRICT \
	--warning_level=VERBOSE \
	--compilation_level=SIMPLE_OPTIMIZATIONS \
	--formatting=SINGLE_QUOTES \
	--jscomp_error=ambiguousFunctionDecl \
	--jscomp_error=checkDebuggerStatement \
	--jscomp_error=checkRegExp \
	--jscomp_error=checkVars \
	--jscomp_error=const \
	--jscomp_error=constantProperty \
	--jscomp_error=es5Strict \
	--jscomp_error=internetExplorerChecks \
	--jscomp_error=invalidCasts \
	--jscomp_error=missingProperties \
	--jscomp_error=suspiciousCode \
	--jscomp_error=undefinedNames \
	--jscomp_error=undefinedVars \
	--jscomp_warning=uselessCode \
	--jscomp_warning=globalThis \
	--jscomp_warning=externsValidation \
	--jscomp_warning=duplicate \
	--jscomp_warning=deprecated \
	--jscomp_warning=accessControls \
	--jscomp_warning=visibility \
	--jscomp_warning=checkTypes \
	--jscomp_warning=fileoverviewTags \
	--jscomp_warning=nonStandardJsDocs \
	--jscomp_warning=strictModuleDepCheck \
	--jscomp_warning=unknownDefines \
#	--output_wrapper='void function(window){ %output %}(window);' \
#	--source_map_format=DEFAULT \
#	--use_types_for_optimization \
#	--create_name_map_files=true \
#	--print_ast \
#	--print_pass_graph \
#	--print_tree \

kobezzza 03.09.2013 11:11

Если я правильно понял, то тебе нужно продекларировать принадлежность методов к прототипу класса, а это можно сделать с помощью директивы lends, но нужно учесть 2 моменты при работе с ней:
1) В директиве указывается ссылка, а не название конструктора или тип, как в type или param
2) Директива применима только для литералов объекта.

Использование:

/** @constructor */
var MyClass = new Class(function init() {

    this.myMethod();

},  /** @lends {MyClass.prototype} */ {

    myMethod: function () { },
    myMethod2: function () { }
});

new MyClass().myMethod();

devote 03.09.2013 11:17

Цитата:

Сообщение от monolithed
Потому что сразу же будет предупреждение:

хм.. а что не так я могу настраивать в GCC? Обычно компилю такой командой:
java -jar compiler.jar --compilation_level ADVANCED_OPTIMIZATIONS --js=Component.class.js --js_output_file=Component.class.min.js


Цитата:

Сообщение от monolithed
мой конфиг:

ужс

monolithed 03.09.2013 12:23

Цитата:

Сообщение от devote
жс

Статический анализ, что тут ужасного?

monolithed 03.09.2013 12:45

Цитата:

Сообщение от kobezzza
сли я правильно понял, то тебе нужно продекларировать принадлежность методов к прототипу класса, а это можно сделать с помощью директивы lends, но нужно учесть 2 моменты при работе с ней

Не совсем так, у меня конструктор создается динамически:

void function() {
     var global = function() {
         return this;
     }();

     var fabric = function(name, value) {
        global[name] = value;
     };

     global.fabric = fabric;
}();

// Как тут сказать что создается конструктор window.foo, а this это ссылка на него ?
fabric('foo', function(name) {
    this[name] = 1;
});

(new foo).prop; // 1



PS: правда такой код успешно компилится, а вот если сделать фабрику конструкторов чуть по сложней, то gcc валит ошибками, что ничего не понимат (


Проблема частично решена, если свойство определено через скобочную нотацию, значит доступ к нему должен быть таким же:

(new foo)['prop']; // 1


Либо экпортировать все переменные заранее, через опцию --externs

monolithed 03.09.2013 23:16

Object.defineProperty(Object, 'isObject', {
	value: function(object) {
		return Object.prototype.toString.call(object) === '[object Object]';
	},
	configurable: true,
	enumerable:   false,
	writable:     true
});

Object.isObject({});

// JSC_INEXISTENT_PROPERTY: Property isObject never defined on Object at line 19 character 0
Object.isObject({});
^



Вот как такое победить чтобы не писать Object['isObject']({});?


Часовой пояс GMT +3, время: 18:52.